【问题标题】:Decay types before passing to std::result_of传递给 std::result_of 之前的衰减类型
【发布时间】:2016-06-15 11:46:32
【问题描述】:

如本页http://en.cppreference.com/w/cpp/thread/async所示,C++14中std::async的签名之一已从C++11版本更改

template< class Function, class... Args>
std::future<typename std::result_of<Function(Args...)>::type>
    async( Function&& f, Args&&... args );

template< class Function, class... Args>
std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>>
    async( Function&& f, Args&&... args );

更改是在传递给std::result_of 之前应用于函数和参数类型的std::decay_ts(删除引用和cv 限定符并将数组/函数衰减为指针)。我不太明白为什么衰变是有用的。例如,对于函数类型Fn(可能是闭包类的类型别名),传递FnFn&amp;&amp;const Fn&amp; 等似乎都会产生相同的结果。

谁能给我一个衰变有用的具体例子吗?

更新:例如,这段代码:

#include <iostream>
#include <type_traits>

int main()
{
    auto fn = [](auto x) -> int { return x + 1; };

    using Fn = decltype(fn);
    using FnRef = Fn&;
    using FnCRef = const Fn&;
    using FnRRef = Fn&&;

    std::cout << std::boolalpha
              << std::is_same<int, std::result_of_t<Fn(int)>>::value << '\n'
              << std::is_same<int, std::result_of_t<FnRef(int)>>::value << '\n'
              << std::is_same<int, std::result_of_t<FnCRef(int)>>::value << '\n'
              << std::is_same<int, std::result_of_t<FnRRef(int)>>::value << '\n';

    return 0;
}

将打印出四个trues。

【问题讨论】:

  • 我认为它有一个引用函数类型的意义。
  • @101010 decay 不去掉引用限定吗?
  • @101010 请看我的更新。

标签: c++ templates c++11 c++14


【解决方案1】:

更改是为了响应LWG 2021。问题是async(如bind等)会衰减复制它的所有参数,所以如果你没有在返回类型中使用decay,你会得到错误的返回类型ref-qualifications 和/或 rvalue-ness:

struct F {
    int operator()() &;
    char operator()() &&;

    int operator(int& ) const;
    char operator(int&& ) const;
};

auto future = std::async(F{}); // actually gives future<int>, but says
                               // it gives future<char>?
auto future2 = std::async(F{}, 1); // ditto

由于 async 的所有参数都被 MoveConstructed 到其内部对象中,因此您需要巧妙地包装它们以实际实现参数的右值性。

这是有道理的——async必须将它的参数存储在某个地方,如果你传入右值,它必须拥有它们的所有权。如果它持有右值引用,则底层对象可能会被破坏。但是一旦将其存储为T,它就不知道它来自T&amp;T&amp;&amp;——它此时只有一个命名的左值参数。

【讨论】:

  • 啊。这些是我们真正热爱 C++ 的原因。
  • 请注意,新版本暗示它moves 将函数和参数从其内部存储中取出到调用表达式中,这也是正确的做法。
  • @Yakk 与bind() 不同的语义使它非常混乱。我意识到线程/异步是 call-one 并且 bind 可能是 call-many,但它仍然......令人困惑。
  • 这就是为什么bind 需要一个右值重载operator() 和一个左值重载operator() 来处理这个问题。对于 lambdas 也是如此:在右值上下文中,它们可能应该在最后一次使用时将其本地状态视为右值或其他东西。另一方面,不确定如何使该语法安全、不令人惊讶和高效,所以......(不令人惊讶是困难的部分!)
  • @ZizhengTai 因为它们被移入 - 作为右值。如果您不decay_t,您将检查返回类型,就好像它们可能是左值一样。
猜你喜欢
  • 2019-04-20
  • 1970-01-01
  • 2017-07-03
  • 2017-06-23
  • 1970-01-01
  • 1970-01-01
  • 2017-02-13
  • 2011-02-10
  • 1970-01-01
相关资源
最近更新 更多