通用引用的语法

fre*_*low 24 c++ language-design rvalue-reference c++11 forwarding-reference

这是一个右值参考:

void foo(int&& a);
Run Code Online (Sandbox Code Playgroud)

它不绑定到左值:

int i = 42;
foo(i);   // error
Run Code Online (Sandbox Code Playgroud)

这是一个普遍的参考:

template<typename T>
void bar(T&& b);
Run Code Online (Sandbox Code Playgroud)

它绑定到右值,它也绑定到左值:

bar(i);   // okay
Run Code Online (Sandbox Code Playgroud)

这是一个右值参考:

template<typename T>
struct X
{
    void baz(T&& c);
};
Run Code Online (Sandbox Code Playgroud)

它不绑定到左值:

X<int> x;
x.baz(i);   // error
Run Code Online (Sandbox Code Playgroud)

为什么通用引用使用与右值引用相同的语法?这不是一个不必要的混乱来源吗?难道该委员会曾经考虑替代语法像T&&&,T&*,T@T&42(开玩笑的对最后一个)?如果是这样,拒绝替代语法的原因是什么?

Jon*_*ely 21

通用引用,例如T&&可以推断T为" 对象类型 "或" 引用类型 "

在你的例子中,它可以推导Tint当传递一个rvalue时,所以函数参数是int&&,或者它可以推导Tint&传递左值时,在这种情况下函数参数是int&(因为参考折叠规则说std::add_rvalue_reference<int&>::type的只是int&)

如果T函数调用没有推导出(如在你的X::baz例子中)那么它不能推导出来int&,因此引用不是通用引用.

所以恕我直言,真的不需要新的语法,它很适合模板参数推导和参考折叠规则,小调整模板参数可以推导为参考类型(其中在C++ 03中的函数模板参数类型T或者T&总是推断T为对象类型.)

这些语义和这种语法是从一开始就提出的,当时rvalue引用和对参数推导规则的调整被提议作为转发问题的解决方案,参见N1385.为了移动语义的目的,提出了使用这种语法提供完美转发的同时提出rvalue引用:N1377N1385在同一邮件中.我认为没有认真提出替代语法.

恕我直言,另一种语法实际上会更加令人困惑.如果您具有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&
Run Code Online (Sandbox Code Playgroud)

或等效地:

struct A {} a;
typedef A& T;
std::add_rvalue_reference<T>::type ref = a;    // type == A&
Run Code Online (Sandbox Code Playgroud)

何时T是左值引用类型,T&&也是.我认为不需要新的语法,规则确实不那么复杂或令人困惑.

  • +1很好解释.斯科特在使用他发明的术语*通用参考*之前联系了我,我表示担心这实际上可能比教导标准实际所说的更加混乱.尽管如此,Scott在成为C++优秀教师方面有着出色的成绩.也许使用他的术语将有助于人们学习.或许这只会引起混乱.我老实说不知道.无论如何,乔纳森解释为什么事情就是这样,并且对论文的引用也很明显. (6认同)
  • 嗯,只是指出,Daveed回答我的温和[上cpp-next.com愤怒(http://cpp-next.com/archive/2009/12/onward-forward/comment-page-1/#comment-270 )说*"我(和其他人)表达了我的偏好不是试图以适应右值引用类型下的一切,而不是具体介绍转发符号"*和*"有我)从一开始的人(如同事,谁想到"移动"和"转发"的合并是一个可怕的想法,我们勾勒出"其他方式"*也*"我的坏在能量向前推动那些草图为与之竞争的提案没有把."* (5认同)

Dav*_* V. 7

为什么通用引用使用与右值引用相同的语法?这不是不必要的混乱根源吗?委员会是否曾经考虑过其他语法...

是的,这令人困惑,IMO(我在这里不同意@JonathanWakely)。我记得在一次非正式讨论(我想是午餐)时,我们确实讨论了不同的符号(Howard Hinnant和Dave Abrahams在那里提出了他们的想法,而EDG的家伙们正在就如何适应它提供反馈)在核心语言中;这早于N1377)。我想我记得&?&|&&曾考虑过,但所有这些都是口头的;我不知道会议记录已被使用(但我相信这也是John建议使用&&用于右值引用)。但是,这些只是设计的早期阶段,当时有很多基本的语义问题需要考虑。(例如,在同一顿午餐讨论中,我们还提出了不使用两种参考,而是使用两种参考参数的可能性。)

在“类模板参数推导”(P0099R3)的C ++ 17功能中找到了引起这种混乱的一个较新的方面。通过转换构造函数和构造函数模板的签名来形成功能模板签名。对于类似:

template<typename T> struct S {
  S(T&&);
};
Run Code Online (Sandbox Code Playgroud)

功能模板签名

template<typename T> auto S(T&&)->S<T>;
Run Code Online (Sandbox Code Playgroud)

形成用于推导类似的声明

int i = 42;
S s = i;  // Deduce S<int> or S<int&>?
Run Code Online (Sandbox Code Playgroud)

推论T = int&这里是违反直觉的。因此,在这种情况下,我们必须添加“特殊扣除规则以禁用特殊扣除规则” :-(