C++ 通用引用的语法
这是一个右值引用:C++ 通用引用的语法,c++,c++11,language-design,rvalue-reference,forwarding-reference,C++,C++11,Language Design,Rvalue Reference,Forwarding Reference,这是一个右值引用: void foo(int&& a); template<typename T> void bar(T&& b); template<typename T> struct X { void baz(T&& c); }; 它不绑定左值: int i = 42; foo(i); // error bar(i); // okay X<int> x; x.baz(i); /
void foo(int&& a);
template<typename T>
void bar(T&& b);
template<typename T>
struct X
{
void baz(T&& c);
};
它不绑定左值:
int i = 42;
foo(i); // error
bar(i); // okay
X<int> x;
x.baz(i); // error
这是一个普遍的参考:
void foo(int&& a);
template<typename T>
void bar(T&& b);
template<typename T>
struct X
{
void baz(T&& c);
};
这是一个右值引用:
void foo(int&& a);
template<typename T>
void bar(T&& b);
template<typename T>
struct X
{
void baz(T&& c);
};
模板
结构X
{
void baz(T&c);
};
它不绑定左值:
int i = 42;
foo(i); // error
bar(i); // okay
X<int> x;
x.baz(i); // error
X;
x、 baz(i);//错误
为什么通用引用使用与右值引用相同的语法?这难道不是不必要的困惑吗?委员会是否考虑过诸如“代码> T&& & /代码>、<代码> T&*>代码>、<代码> T @ <代码>或<代码> T和42 (只在最后一个上开玩笑)的替代语法?如果是这样的话,拒绝替代语法的原因是什么?像
T&
这样的通用引用可以推断T
为“对象类型”或“引用类型”
在您的示例中,当传递一个右值时,它可以将T
推断为int
,因此函数参数是int&
,或者当传递一个左值时,它可以将T
推断为int&
,在这种情况下,函数参数是int&
(因为引用折叠规则说,std::add\u rvalue\u reference::type
只是int&
)
如果函数调用未推导出T
(如X::baz
示例中所示),则无法将其推导为int&
,因此该引用不是通用引用
因此,实际上不需要新语法,它非常适合模板参数推断和引用折叠规则,只需稍加调整,模板参数就可以推断为引用类型(其中,在C++03中,T
或T&
类型的函数模板参数总是将T
推断为对象类型。)
这些语义和此语法是从一开始就提出的,当时提出了右值引用和对参数推断规则的调整,作为转发问题的解决方案,请参阅。使用此语法提供完美转发的建议与出于移动语义目的提出右值引用的建议是并行的:wa它和N1385在同一封邮件中。我不认为有人认真地提出过其他语法
如果你有template void bar(T&@)
作为通用参考的语法,但语义与我们今天的相同,那么在调用bar(i)时
模板参数T
可以推断为int&
或int
,函数参数的类型为int&
或int&
…两者都不是“T&
”(无论是什么类型)因此,您可以使用声明符T&@
的语言中的语法,它不是一个可以存在的类型,因为它实际上总是引用其他类型,或者int&
或者int&
至少在语法方面,我们得到的类型T&&
是一个实类型,引用折叠规则并不特定于使用通用引用的函数模板,它们与模板之外的其他类型系统完全一致:
struct A {} a;
typedef A& T;
T&& ref = a; // T&& == A&
或相当于:
struct A {} a;
typedef A& T;
std::add_rvalue_reference<T>::type ref = a; // type == A&
结构A{}A;
类型定义A&T;
std::add\u rvalue\u reference::type ref=a;//type==a&
当T
是左值引用类型时,T&
也是。我认为不需要新的语法,规则真的没有那么复杂或混乱
为什么通用引用使用与RValk引用相同的语法?这不是一个不必要的混淆来源吗?委员会是否考虑过替代语法?
是的,这很令人困惑,IMO(我不同意这里的@JonathanWakely)。我记得在一次非正式讨论(我想是午餐)中,我们讨论了整体功能的早期设计,讨论了不同的符号(Howard Hinnant和Dave Abrahams带来了他们的想法,EDG的人就如何将其融入核心语言给出了反馈;这早于N1377)。我想我记得&
和&&&
都被考虑过,但所有这些都是口头的;我不知道有人做过会议记录(但我相信这也是约翰建议使用Reals> & & <代码)来进行R值引用的时候。然而,这些都是设计的早期阶段,并且当时有很多基本的语义问题需要考虑。(例如,在同一次午餐讨论中,我们还提出了不使用两种引用,而是使用两种引用参数的可能性。)
这导致混淆的一个更为近期的方面是C++17的“类模板参数推断”(P0099R3)功能。函数模板签名是通过转换构造函数和构造函数模板的签名形成的。例如:
template<typename T> struct S {
S(T&&);
};
template <typename T>
void foo (T&& whatever) {...}
模板结构{
S(T&),;
};
函数模板签名
template<typename T> auto S(T&&)->S<T>;
template auto S(T&)->S;
用于推导声明,如
int i = 42;
S s = i; // Deduce S<int> or S<int&>?
inti=42;
S=i;//推断S还是S?
在这里推断
T=int&
是违反直觉的。因此,在这种情况下,我们必须添加“特殊推断规则以禁用特殊推断规则”:-(作为一个只有几年C++经验的开发者,我必须同意普遍引用是令人困惑的。在我看来,这些东西是完全不可能理解的,更不用说积极地阅读Scott Meyers和/或看YouTube上的相关谈话了。” 不仅仅是&&可以是r值参考或“通用参考”,还可以是模板用于区分参考类型的方式。假设您是从事软件开发的普通工程师。您阅读了以下内容:
template<typename T> struct S {
S(T&&);
};
template <typename T>
void foo (T&& whatever) {...}
模板
void foo(T&&无论什么){…}
然后foo的函数体告诉您,输入参数必须是一个非常特定的类类型