C++ 为什么使用std::forward<;T>;而不是静态_cast<;T&&&燃气轮机;
当给定以下结构的代码时C++ 为什么使用std::forward<;T>;而不是静态_cast<;T&&&燃气轮机;,c++,templates,c++17,rvalue-reference,forwarding,C++,Templates,C++17,Rvalue Reference,Forwarding,当给定以下结构的代码时 template <typename... Args> void foo(Args&&... args) { ... } 模板 void foo(Args&&…Args){…} 我经常看到库代码使用static\u cast),这会以这样一种方式折叠引用,结果是一个右值。使用的规则是&&&&&->&&(上面的规则1) 当给定左值引用时,这会以这样的方式折叠引用,结果是左值。这里使用的规则是&&->&(上面的规则2) 这实质上是让foo()
template <typename... Args>
void foo(Args&&... args) { ... }
模板
void foo(Args&&…Args){…}
我经常看到库代码使用static\u cast),这会以这样一种方式折叠引用,结果是一个右值。使用的规则是&&&&&
->&&
(上面的规则1
)
当给定左值引用时,这会以这样的方式折叠引用,结果是左值。这里使用的规则是&&
->&
(上面的规则2
)
这实质上是让foo()
将参数转发到上面示例中的bar()
。这是使用std::forward指定的6个约束时会出现的行为,在这些约束下,std::forward
的任何实现都应该“正确”运行。这些是
应将左值作为左值转发
应将右值作为右值转发
不应将右值作为左值转发
应将较少的cv限定表达式转发给较多的cv限定表达式
应该将派生类型的表达式转发到可访问的、明确的基类型
不应转发任意类型转换
(1) 和(2)被证明在上述static_cast
中正常工作。(3) -(6)此处不适用,因为在推导的上下文中调用函数时,这些都不会发生
注意:我个人更喜欢使用std::forward
,但我的理由纯粹是我更喜欢遵守惯例 这里是我的2.5版,技术上不那么重要:今天的std::forward
实际上只是一个普通的static\u cast
并不意味着明天它也将以完全相同的方式实现。我认为委员会需要一些东西来反映std::forward
今天所达到的理想行为,因此,在任何地方都不转发任何内容的forward
应运而生
通过在std::forward
的保护伞下形式化所需的行为和期望,仅从理论上讲,没有人会阻止未来的实现者将std::forward
作为自己的具体实现而不是静态转换
,实际上没有考虑到static\u cast
,因为唯一重要的事实是std::forward的正确使用和行为Scott Meyers说std::forward
和std::move
主要是为了方便。他甚至指出,std::forward
可用于执行std::forward
和std::move
“有效的现代C++”的一些摘录:
第23项:理解std::move和std::forward
…
std::forward
的故事与std::move
的故事类似,但是std::move
无条件地将其参数转换为rvalue
,std::forward
仅在特定条件下执行<代码>标准::转发
是一种条件转换。仅当其参数初始化为rvalue
时,它才会强制转换为rvalue
…
鉴于std::move
和std::forward
都可以归结为强制类型转换,唯一的区别是std::move
总是强制类型转换,而std::forward
只是偶尔才会强制类型转换,你可能会问我们是否可以省去std::move
而在任何地方都使用std::forward
。从纯粹的技术角度来看,答案是肯定的:std::forward
可以做到这一切<代码>标准::移动
不是必需的当然,这两个函数都不是必需的,因为我们可以在任何地方编写强制类型转换,但我希望我们都同意这一点,嗯,很恶心。
…
std::move
的吸引力在于方便、减少出错的可能性和更清晰
对于那些感兴趣的人,比较std::forward
vsstatic\u cast
in(无任何优化)当使用lvalue
和rvalueforward
调用时,表示意图,使用它可能比使用static\u cast
更安全:static\u 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
}
结构A{
A(int);
};
模板
Arg1&&f(Arg1&&a1、Arg2&&a2){
返回静态输出
如何应用这种正当性:通常,这种正当性的理由是使用静态_cast可以避免不必要的模板实例化
编译时间比代码的可维护性问题更大吗?程序员是否应该浪费时间来考虑最小化代码中每一行的“不必要的模板实例化”
当模板被实例化时,其实例化会导致在其定义和声明中使用的模板的实例化。因此,例如,如果您具有以下函数:
template<class T> void foo(T i){
foo_1(i),foo_2(i),foo_3(i);
}
模板无效foo(ti){
富1(i),富2(i),富3(i);;
}
<> > <代码> FooGy1,<代码> FooY2,<代码> FooY3是模板,“代码> FoO < /代码>的实例化将导致3个实例化。然后递归地,如果这些函数导致其他3个模板函数的实例化,例如,您可以得到3×3=9个实例化。一个根函数实例化可能会导致数千个实例化,产生指数级增长的涟漪效应。另一方面,像forward
这样的函数是该实例化树中的一片叶子。因此,避免其实例化可能只会避免一个实例化
因此,避免模板实例化爆炸的最佳方法是使用动态