这是一个右值参考:
void foo(int&& a);
它不会绑定到左值:
int i = 42;
foo(i); // error
这是一个普遍的参考:
template<typename T>
void bar(T&& b);
它与右值结合,它也与左值结合:
bar(i); // okay
这是一个右值参考:
template<typename T>
struct X
{
void baz(T&& c);
};
它不会绑定到左值:
X<int> x;
x.baz(i); // error
为什么通用引用使用与rvalue引用相同的语法?这不是一个不必要的混乱来源吗?委员会是否考虑过T&&&
,T&*
,T@
或T&42
之类的替代语法(只是在最后一个开玩笑)?如果是这样,拒绝替代语法的原因是什么?
答案 0 :(得分:21)
T&&
等通用引用可以将T
推断为“对象类型”,或“引用类型”
在您的示例中,它可以在传递rvalue时将T
推导为int
,因此函数参数为int&&
,或者它可以将T
推导为int&
当传递一个左值时,在这种情况下函数参数是int&
(因为参考折叠规则说std::add_rvalue_reference<int&>::type
只是int&
)
如果函数调用未推导出T
(如X::baz
示例中所示),则无法将其推断为int&
,因此参考不是通用的参考
所以恕我直言,真的不需要新的语法,它很适合模板参数推导和参考折叠规则,可以推导出模板参数作为参考类型的小调整(在C ++ 03中一个函数模板)类型为T
或T&
的参数始终会将T
推断为对象类型。)
这些语义和这种语法是从一开始就提出的,当时rvalue引用和对参数推导规则的调整被提议作为转发问题的解决方案,请参阅N1385。为了移动语义的目的,提出了使用这种语法提供完美转发的同时提出了右值引用:N1377与N1385在同一个邮件中。我认为没有认真提出替代语法。
恕我直言,无论如何,替代语法实际上会更加混乱。如果您使用template<typename T> 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&
当T
是左值参考类型时,T&&
也是。我认为不需要新的语法,规则确实不那么复杂或令人困惑。
答案 1 :(得分:6)
为什么通用引用使用与rvalue引用相同的语法?这不是一个不必要的混乱来源吗?委员会是否考虑过替代语法......
是的,这很令人困惑,IMO(我不同意@JonathanWakely在这里)。我记得在一次关于整体特征的早期设计的非正式讨论(午餐,我认为)中,我们讨论了不同的符号(Howard Hinnant和Dave Abrahams在那里带来了他们的想法,EDG人员正在提供关于它如何适合的反馈在核心语言中;这早于N1377)。我想我记得&?
和&|&&
被认为是,但所有这些都是口头的;我不知道已经采取了会议记录(但我相信这也是约翰建议使用&&
进行右值引用时)。然而,那些是设计的早期阶段,当时有许多基本的语义问题需要考虑。 (例如,在同一个午餐讨论期间,我们也提出了不存在两种参考的可能性,而是有两种参考参数。)
这引起混淆的最新方面可以在“类模板参数推导”(P0099R3)的C ++ 17特性中找到。通过转换构造函数和构造函数模板的签名来形成函数模板签名。对于类似的事情:
template<typename T> struct S {
S(T&&);
};
功能模板签名
template<typename T> auto S(T&&)->S<T>;
形成用于扣除诸如
之类的声明int i = 42;
S s = i; // Deduce S<int> or S<int&>?
在这里推断T = int&
会违反直觉。因此,在这种情况下,我们必须添加“特殊扣除规则来禁用特殊扣除规则”: - (
答案 2 :(得分:0)
作为只有几年C ++经验的开发人员,我必须同意通用引用令人困惑。在我看来,这些东西是完全不可能理解的,更不用说不阅读Scott Meyers和/或在YouTube上观看相关演讲而积极使用了。
不仅&&可以是r值引用,也可以是“通用引用”,这是模板用来区分引用类型的方式。假设您是一名从事软件开发的普通工程师。您读到以下内容:
template <typename T>
void foo (T&& whatever) {...}
,然后foo的函数主体告诉您,输入参数必须是内部库中定义的非常特定的类类型。您认为很好,该模板在当前软件版本中已过时,可能是以前开发中的遗留物,所以让我们摆脱它。
祝您编译器出现错误……
尚未明确了解这一点的任何人都不会假设您将foo作为函数模板实现,只是为了采用模板推导规则和引用碰巧一致的折叠规则,从而最终得到所谓的“完美转发” ”。实际上,它看起来更像是一个肮脏的hack。我认为为通用引用创建语法将使事情变得更简单,并且曾经在较旧代码库上工作的开发人员至少会知道必须在这里搜索 。只是我的两分钱。