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
    vs
    static\u cast
    in(无任何优化)当使用
    lvalue
    rvalue

    forward
    调用时,表示意图,使用它可能比使用
    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
    这样的函数是该实例化树中的一片叶子。因此,避免其实例化可能只会避免一个实例化

    因此,避免模板实例化爆炸的最佳方法是使用动态