C++ 通过通用引用捕获

C++ 通过通用引用捕获,c++,lambda,c++14,C++,Lambda,C++14,当将推导出的类型作为r值引用传递时,我获得了通用引用功能,并且可以像这样构建完美的转发: template <typename T> void func(T&& t) { other_func(std::forward<T>(t)); } 模板 无效函数(T&T){ 其他功能(标准:转发(t)); } …由于T的推导方式和标准的参考折叠规则 现在考虑另一个FUNC取一个函数对象< /P> template <typename T>

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

template <typename T>
void func(T&& t) {
    other_func(std::forward<T>(t));
}
模板
无效函数(T&T){
其他功能(标准:转发(t));
}
…由于T的推导方式和标准的参考折叠规则

现在考虑另一个FUNC取一个函数对象< /P>

template <typename T>
void func(T&& t) {
    other_func([](int v) { return t + v; }); // I chose addition for example purposes
}
模板
无效函数(T&T){
other_func([](int v){return t+v;});//我选择加法作为示例
}
现在很明显,由于没有捕获到t,所以无法编译。我的问题是:我如何捕捉它,从而捕捉到的值将是T的推断值

这是否可能使用新的通用lambda捕获?如果。。。怎么做

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

我仍然不太了解新捕获初始值设定项的机制…

好的,让我们试试这个。不幸的是,我手头没有一个支持这个特性的编译器,所以如果我在这个过程中严重误解了一些东西,请原谅我

有关这方面的建议是

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

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

因此,从捕获列表中得到什么的问题相当于从声明中得到什么

这里的推断类型是(引用限定符由
auto
删除),因此您总是会在这里得到一个新值。如果
t
是右值引用,则将移动构造该新值,否则将复制构造(由于
std::forward
的返回值)

好的是,这个新值由lambda拥有。因此,无论最初传入的是什么
t
,从捕获的
c
移动
std::都是安全的。因此,即使您不再知道初始
t
的类型,您仍然没有丢失任何东西。

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

模板
无效函数(T&T){
其他函数([&t](int v){
返回标准::正向(t)+v;
});
}

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

模板
无效函数(T&T){
其他函数([&t](intv){return std::forward(t)+v;});
}
相反,如果决定使用值捕获,则lambda应标记为可变以允许有效移动(因为常量限定符隐式添加到lambda):

模板
无效函数(T&T){
其他函数([t=std::forward(t)](int v)可变{return std::move(t)+v;});
}

我认为您需要一个包装器类型来支持完美的转发。右值引用成员将不允许复制,并且移动构造函数不需要对lambda可用。@dyp是的,包装类型可以工作。但是,它也会非常难看(
std::auto_ptr_ref
,有人吗?)。有趣的是,我认为在这里牺牲完美转发不会带来任何实质性损失:如果传入右值或副本,我们仍然可以只进行移动。这是真的,但移动也可能很昂贵,例如,当成员仅为副本时(旧数据类型、数组等等)。我想到了类似于
模板包装器{tm;};模板自动换行(T&&p)->换行器{return{std::forward(p)}}
然后
[T=wrap(std::forward(T))]
)感谢您的研究工作(UglyFontMS;)再加上dyp建议的包装器,这对我来说绝对是一个解决方案。你可以考虑把它加入到你的答案中——无论哪种方式,我都会接受它。FYI,ClRU+ + Celru是3.4版本后不久的一个主干Rev,所以它支持大多数/所有C++ 14语言特性。这很有趣。另请注意:在运算符声明之后编写的引用说明符的名称是什么?我不知道这些exist@RichardVock那些是。与成员函数上的
const
-限定符非常相似,它们约束可以调用函数的对象。例如,当您在右值对象上调用
foo
时,会调用
void foo()&&
。可能的问题是:如果
other_func
存储lambda,并且它的持续时间长于
func
的主体范围,调用lambda是UB,因为捕获的变量
t
已经离开了作用域。我发现这个问题之所以如此,是因为我正在搜索类似
[&=t]
(或
[&=]
的内容。我的意思是——引用类型将通过引用保存在lambda的
this
中,值类型或右值引用类型的变量将通过值保存。这样可能吗?这样,可能的范围问题(在上面的评论中)就不会那么麻烦了。特别是在我的例子中,假设没有
other_func
,而是在
func
中返回lambda。如果T是引用类型,则不会进行复制,但是如果T&&是右值,则它会安全地存储在lambda的
this
中。我指定的用例实际上是编写返回范围的函数(
range/v3
库)时的常见用例。函数接受参数(引用或值类型),转换/调整用户定义的lambda几乎总是希望使用所有参数。因此,对于每个转换lambda,当前的解决方案是通过引用或值手动捕获所有参数,基本上是重复
template <typename T>
void func(T&& t) {
  other_func([&t](int v) {
    return std::forward<T>(t) + v;
  });
}
template <typename T>
void func(T&& t) {
    other_func([&t](int v) { return  std::forward<T>(t) + v; });
}
template <typename T>
void func(T&& t) {
    other_func([t = std::forward<T>(t)](int v) mutable { return  std::move(t) + v; });
}