通过通用参考捕获

时间:2014-01-20 15:56:51

标签: c++ lambda c++14

当将推断类型作为r值引用传递时,我获得了通用引用功能,并且可以像这样完美转发:

template <typename T>
void func(T&& t) {
    other_func(std::forward<T>(t));
}

...由于T的推导方式和标准的参考折叠规则。

现在考虑other_func采用函数对象

template <typename T>
void func(T&& t) {
    other_func([](int v) { return t + v; }); // I chose addition for example purposes
}

现在显然由于未被捕获而无法编译。我的问题是:我如何捕获它,以便捕获的值将是推导出的T?

这是否可以使用新的通用lambda捕获?如果......怎么样?

[t = std::forward<T>(t)] ? 

我仍然没有真正掌握新捕获初始化器的机制......

3 个答案:

答案 0 :(得分:8)

好的,我们来试试吧。不幸的是,我手头没有支持这个功能的编译器,所以如果我在整个过程中严重误解了这些内容,请原谅我。

涉及此问题的提案是N3648

这里有趣的部分是,使用auto推导出init捕获中变量的类型:

  

该成员的类型对应于假设的类型   “auto init-capture”形式的变量声明[...]。

因此,您从捕获列表[c = std::forward<T>(t)]获得的内容的问题等同于您从声明auto c = std::forward<T>(t)获得的内容。

此处推断的类型将为std::remove_reference<T>::type(参考限定符将被auto删除),因此您将始终在此处获得新值。如果t是右值引用,您将移动构造该新值,否则您将复制构造(由于std::forward的返回值)。

好消息是这个新值由lambda拥有。因此,无论您最初传入的t是什么,std::move对于已捕获的c都是安全的。所以,即使你不知道最初t的类型,你仍然没有失去任何东西。

答案 1 :(得分:7)

您可以在C ++ 11中“通过通用引用捕获”,因为模板参数T的类型可用于lambda函数(hideous live code example at Coliru):

template <typename T>
void func(T&& t) {
  other_func([&t](int v) {
    return std::forward<T>(t) + v;
  });
}

答案 2 :(得分:3)

为了通过引用捕获实现所需的行为,不需要C ++ 14的通用lambda捕获(但是一如既往地通过引用捕获,需要注意不要创建悬空引用):

template <typename T>
void func(T&& t) {
    other_func([&t](int v) { return  std::forward<T>(t) + v; });
}

相反,如果决定使用按值捕获,则应将lambda标记为可变以允许有效移动(因为const限定符隐式添加到lambda中):

template <typename T>
void func(T&& t) {
    other_func([t = std::forward<T>(t)](int v) mutable { return  std::move(t) + v; });
}