Cur*_*ous 23 c++ templates forwarding rvalue-reference c++17
当给出以下结构的代码时
template <typename... Args>
void foo(Args&&... args) { ... }
Run Code Online (Sandbox Code Playgroud)
我经常static_cast<Args&&>
在函数中看到库代码用于参数转发.通常,对此的理由是使用static_cast
避免不必要的模板实例化.
鉴于语言的参考折叠和模板扣除规则.我们得到了完美的转发,static_cast<Args&&>
这个声明的证据如下(在误差范围内,我希望答案会启发)
&& &&
- > &&
(1
上面的规则)& &&
- > &
(2
上面的规则)这基本上foo()
是bar()
在上面的例子中转发参数.这也是你在std::forward<Args>
这里使用时会得到的行为.
问题 -为什么要std::forward
在这些背景下使用?避免额外的实例化是否有理由违反惯例?
Howard Hinnant的论文n2951规定了6个约束,在这些约束条件下,任何实现都std::forward
应该"正确".这些曾经是
(1)和(2)证明与static_cast<Args&&>
上述方法一起正常工作.(3) - (6)这里不适用,因为在推导的上下文中调用函数时,这些都不会发生.
注意:我个人更喜欢使用std::forward
,但我的理由纯粹是我更喜欢坚持惯例.
P.W*_*P.W 13
斯科特迈尔斯说,std::forward
和std::move
主要是为了方便.他甚至指出,std::forward
可用于同时执行的功能std::forward
和std::move
.
"Effective Modern C++"的一些摘录:
第23项:理解std :: move和std :: forward
......
故事std::forward
类似于forstd::move
,但是std::move
无条件地将其论证转换为arvalue
,std::forward
仅在某些条件下才会这样做.std::forward
是条件演员.rvalue
只有当它的参数用a初始化时才转换为rvalue
.
......
考虑到两者std::move
并std::forward
归结为演员阵容,唯一的区别在于std::move
总是演员阵容,而std::forward
有时候只是,你可能会问我们是否可以放弃std::move
并且只是std::forward
在任何地方使用.从纯粹的技术角度来看,答案是肯定的:std::forward
可以做到这一切.std::move
没有必要.当然,这两种功能都不是必需的,因为我们可以在任何地方写出演员,但我希望我们同意那将是,令人讨厌的.
...
std::move
的吸引力是方便,错误的可能性降低,清晰度更高......
对于那些有兴趣,比较std::forward<T>
VS static_cast<T&&>
在装配时,一个名为(不使用任何优化)lvalue
和rvalue
.
forward
表达意图并且使用起来可能比使用更安全static_cast
:static_cast
考虑转换,但检测到一些危险的和所谓的非故意转换forward
:
struct A{
A(int);
};
template<class Arg1,class Arg2>
Arg1&& f(Arg1&& a1,Arg2&& a2){
return static_cast<Arg1&&>(a2); // typing error: a1=>a2
}
template<class Arg1,class Arg2>
Arg1&& g(Arg1&& a1,Arg2&& a2){
return forward<Arg1>(a2); // typing error: a1=>a2
}
void test(const A a,int i){
const A& x = f(a,i);//dangling reference
const A& y = g(a,i);//compilation error
}
Run Code Online (Sandbox Code Playgroud)
错误消息示例:编译器资源管理器链接
如何应用这种理由:通常,这样做的理由是使用static_cast可以避免不必要的模板实例化.
编译时间比代码可维护性更有问题吗?编码员是否应该考虑尽量减少代码中每一行的"不必要的模板实例化"?
实例化模板时,其实例化会导致在其定义和声明中使用的模板实例化.所以,例如,如果你有一个函数:
template<class T> void foo(T i){
foo_1(i),foo_2(i),foo_3(i);
}
Run Code Online (Sandbox Code Playgroud)
其中foo_1
,foo_2
,foo_3
是模板,的实例化foo
将导致3个实例.然后递归地,如果这些函数导致其他3个模板函数的实例化,例如,您可以获得3*3 = 9个实例化.因此,您可以将此实例化链视为一个树,其中根函数实例化可以导致数千个实例化作为指数增长的涟漪效应.另一方面,函数就像forward
是这个实例化树中的叶子.因此,避免其实例化可能只能避免1次实例化.
因此,避免模板实例化爆炸的最佳方法是对"root"类和"root"函数的参数类型使用动态多态,然后仅对在此实例化树中实际上位的时间关键函数使用静态多态.
所以,在我看来,与使用更具表现力(和更安全)的代码相比,使用static_cast
到位forward
来避免实例化是一种时间的浪费.在代码体系结构级别更有效地管理模板实例化爆炸.
这是我的2.5,不是那么技术上的分数:今天std::forward
确实只是一个普通的旧static_cast<T&&>
事实并不意味着明天它也将以完全相同的方式实施.我认为委员会需要一些东西来反映std::forward
今天所取得的成果所期望的行为,而forward
这种行为并没有随处出现.
随着具有所要求的行为和期望的保护伞下形式化std::forward
,只是从理论上讲,没有人阻碍未来的实施者提供std::forward
的不是static_cast<T&&>
,但是具体自己实现的东西,实际上并没有考虑到static_cast<T&&>
,因为唯一重要的事实是正确的用法和行为std::forward
.
归档时间: |
|
查看次数: |
1429 次 |
最近记录: |