Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 当你完善前进时,typename T变成T&;或T&&;,但当你不';t、 t是';这根本不是参考资料。怎么用?_C++_Templates_C++11 - Fatal编程技术网

C++ 当你完善前进时,typename T变成T&;或T&&;,但当你不';t、 t是';这根本不是参考资料。怎么用?

C++ 当你完善前进时,typename T变成T&;或T&&;,但当你不';t、 t是';这根本不是参考资料。怎么用?,c++,templates,c++11,C++,Templates,C++11,我正在读关于完美转发的书,我学到了一些让我困惑的东西:当你试图实现完美转发时,你会这样做: template<class T> // If I were to call foo with an l-value int, foo would look void foo(T &&x); // like this: void foo(int& &&x) template//如果我用l值int调用foo,foo会 void foo(T&x);//如

我正在读关于完美转发的书,我学到了一些让我困惑的东西:
当你试图实现完美转发时,你会这样做:

template<class T> // If I were to call foo with an l-value int, foo would look
void foo(T &&x);  // like this: void foo(int& &&x)
template//如果我用l值int调用foo,foo会
void foo(T&x);//如下所示:voidfoo(int&&x)
然后我想,等等,这是否意味着如果我这样做:

template<class T> // If I were to call foo with an l-value int, foo would look
void foo(T x);    // like this: void foo(int& x);
template//如果我用l值int调用foo,foo会
void foo(T x);//像这样:voidfoo(int&x);
但事实并非如此。foo看起来像这样:
voidfoo(intx)


我的问题:为什么在完美转发函数中,T会变成T&或T&&而在另一个函数中,T不是引用?有人能告诉我具体的规则吗?我需要一些澄清

模板参数
T
仅当出现在
T&

表单的函数模板:

  • template void f(T x)
    T
    推断为对象类型(并且
    x
    是对象类型,因此通过值传递)

  • template void f(T&x)
    T
    推断为对象类型(然后
    x
    具有左值引用类型)

  • template void f(T&&x)
    将推断
    T

    • 左值引用(因此
      x
      由于引用折叠规则而具有左值引用类型)
    • 或者作为对象类型(因此
      x
      具有右值引用类型)
如何在完美的转发功能中,T变成T&或T&,[…]

这是错误的
T
成为参考类型
L&
对象类型
R
,而不是参考
R&

因此,形式
T&
的函数参数变为

  • 要么
    L&
    (因为向左值引用添加右值引用仍然是左值引用,就像
    add\u右值引用::type
    仍然是
    L&
  • 或者变成
    R&
    (因为
    add\u-rvalue\u-reference::type
    R&

这是因为定义类型推断的方式,它只与完美转发相关,因为传递右值引用时,
std::forward()
的结果是右值,传递左值引用时是左值

但一般来说,当您没有引用开始时,您的
T
不会被推断为引用类型(即
a&
,无论
a
可能是什么)。如果是这样的话,正如注释中正确指出的那样,就不可能编写一个按值获取参数的函数模板

特别是,您所指的引用折叠规则在C++11标准的第14.8.2.1/4段中有定义:

如果p是a 引用类型,P引用的类型用于类型推断如果P是对cv的右值引用 模板参数,并且参数是左值,在 类型扣除的地点。[示例:

模板int f(T&&);
模板int g(常数T&&);
int i;
int n1=f(i);//调用f(int&)
int n2=f(0);//调用f(int&&)
int n3=g(i);//错误:将调用g(const int&&),其中
//将右值引用绑定到左值
-[结束示例]


在推导模板参数时,一般需要考虑三种情况:

  • void foo(tx)
    :这表示“通过值传递”。它总是根据需要推断出一个类型来传递值

  • void foo(T&x)
    :这意味着“通过左值引用传递”。它总是推断出一个适当的类型以通过左值引用传递

  • void foo(T&&x)
    :这意味着“通过引用传递”。它总是根据引用来推断适当的类型,可以是左值引用或右值引用


  • 放松,放慢速度,呼吸

    模板参数推导是您需要了解的核心机制,它不是完全琐碎的。当你说
    template void foo(T)
    时,
    T
    总是被推断为非引用类型

    如果需要引用,则必须在其上放置
    &
    模板void foo(T&)
    也会将
    T
    推断为非引用类型,但
    foo
    现在总是需要左值引用

    最后一个魔法来自新的引用折叠规则。当您说
    template void foo(T&&)
    时,可能会发生两件事:

    • 使用右值调用
      foo
      ,例如
      foo(Bar())
      。然后将
      T
      推导出为
      Bar
      foo
      Bar
      进行右值引用,即
      Bar&

    • 使用左值调用
      foo
      ,例如
      条x;foo(x)。现在,
      foo
      唯一可以接受的就是左值引用。这要求将
      T
      推断为
      Bar&
      ,因为
      T&&==Bar&&&&==Bar&
      ,这是由于折叠规则


    只有这个最终模板才能同时接受左值和右值。这就是为什么它有时被称为“普遍参照”;但请记住,重要的不是引用,而是模板参数推断。使用
    std::forward
    可以传递与接收到的值类别相同的参数。

    为什么也可能有用。如果
    模板void foo(T)
    可以推断
    T=int&template <class T> int f(T&&);
    template <class T> int g(const T&&);
    int i;
    int n1 = f(i); // calls f<int&>(int&)
    int n2 = f(0); // calls f<int>(int&&)
    int n3 = g(i); // error: would call g<int>(const int&&), which
    // would bind an rvalue reference to an lvalue