如何在C++14中编写泛型转发lambda?
尝试#1
[](auto&& x) { return x; }在函数体中,x是一个lvalue,所以这是不起作用的。
尝试#2
[](auto&& x) { return std::forward<decltype(x)>(x); }这正确地转发了lambda中的引用,但是它总是按值返回(除非编译器删除副本)。
尝试#3
[](auto&& x) -> decltype(x) { return std::forward<decltype(x)>(x); }这将返回与参数相同的类型(可能-> auto&&也会工作),并且看起来工作正常。
尝试#4
[](auto&& x) noexcept -> decltype(x) { return std::forward<decltype(x)>(x); }添加noexcept是否使此lambda更适用,从而严格地优于#3?
发布于 2015-05-07 07:00:07
返回类型的decltype(x)是不够的。
可以隐式地将由值获取的局部变量和函数参数移到返回值中,但不可以将由rvalue引用获取的函数参数(x是一个lvalue,即使如果传递rvalue则为decltype(x) == rvalue引用)。委员会给出的理由是,他们希望确信,当编译器隐式移动时,其他人不可能拥有它。这就是为什么我们可以从prvalue (临时的、非引用的限定返回值)和函数本地值移动。然而,有些人可以做一些愚蠢的事情,比如
std::string str = "Hello, world!";
f(std::move(str));
std::cout << str << '\n';而且委员会不想默默地引用不安全的举动,认为他们应该从这个新的“移动”特性开始变得更加保守。请注意,在C++20中,这个问题将得到解决,您可以简单地执行return x,它将做正确的事情。请参阅http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0527r0.html
对于转发函数,我会尝试使noexcept正确。这里很简单,因为我们只是处理引用(它是无条件的noexcept)。否则,您会破坏关心noexcept的代码。
这使得最终理想的转发lambda看起来如下:
auto lambda = [](auto && x) noexcept -> auto && { return std::forward<decltype(x)>(x); };返回类型的decltype(auto)在这里会做同样的事情,但是auto &&更好地记录了这个函数总是返回一个引用。它还避免再次提到变量名,我认为这会使重命名变量稍微容易一些。
在C++17中,转发lambda在可能的情况下也是隐式的。
发布于 2015-05-07 13:45:41
我能生成的最少的字符,但功能完整的版本是:
[](auto&&x)noexcept->auto&&{return decltype(x)(x);}这使用了一个我认为有用的成语--当在lambda中转发auto&&参数时,执行decltype(arg)(arg)。如果您知道arg是引用类型的,则通过decltype转发forward是相对没有意义的。
如果x是值类型的,则decltype(x)(x)实际上生成x的副本,而std::forward<decltype(x)>(x)则生成对x的rvalue引用。因此,decltype(x)(x)模式在一般情况下不如forward有用:但这不是一般情况。
auto&&将允许返回引用(与传入的引用匹配)。遗憾的是,引用生存期扩展不能正确地处理上面的代码--我发现将rvalue引用转发到rvalue引用通常是错误的解决方案。
template<class T>struct tag{using type=T;};
template<class Tag>using type=typename Tag::type;
template<class T> struct R_t:tag<T>{};
template<class T> struct R_t<T&>:tag<T&>{};
template<class T> struct R_t<T&&>:tag<T>{};
template<class T>using R=type<R_t<T>>;
[](auto&&x)noexcept->R<decltype(x)>{return decltype(x)(x);}给了你这种行为。lvalue引用变成lvalue引用,rvalue引用变成值。
https://stackoverflow.com/questions/30091288
复制相似问题