27

这是一个右值引用:

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

为什么通用引用使用与右值引用相同的语法?这不是不必要的混乱来源吗?委员会是否考虑过替代语法,例如,T&&&或(只是在开玩笑)?如果是这样,拒绝替代语法的原因是什么?T&*T@T&42

4

3 回答 3

22

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

在您的示例中,它可以推断Tint传递右值时,因此函数参数是int&&,或者它可以推断Tint&传递左值时,在这种情况下,函数参数是int&(因为参考折叠规则说std::add_rvalue_reference<int&>::typeis just int&

如果T不是由函数调用推断出(如您的X::baz示例中),则不能推断为int&,因此该引用不是通用引用。

所以恕我直言,真的不需要新语法,它非常适合模板参数推导和引用折叠规则,只需稍加调整即可将模板参数推导出为引用类型(在 C++03 中,函数模板参数类型T或者T&总是推断T为对象类型。)

这些语义和这种语法是从一开始就提出的,当时提出了右值引用和对参数推导规则的调整作为转发问题的解决方案,请参阅N1385。使用这种语法来提供完美的转发是在提出右值引用的同时提出的,目的是为了移动语义:N1377与 N1385在同一个邮件中。我认为从未认真提出过替代语法。

恕我直言,无论如何,另一种语法实际上会更加混乱。如果您有template<typename T> void bar(T&@)通用引用的语法,但语义与我们今天的语义相同,那么在调用bar(i)模板参数时T可以推断为int&orint并且函数参数的类型为int&or 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&&也是。我认为不需要新的语法,规则真的没有那么复杂或混乱。

于 2013-01-13T14:03:50.037 回答
7

为什么通用引用使用与右值引用相同的语法?这不是不必要的混乱来源吗?委员会有没有考虑过替代语法......

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

在“类模板参数推导”(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&是违反直觉的。因此,在这种情况下,我们必须添加“特殊扣除规则以禁用特殊扣除规则”:-(

于 2016-12-12T00:52:07.243 回答
0

作为一个只有几年 C++ 经验的开发人员,我必须同意通用引用令人困惑。在我看来,这些东西完全无法理解,更不用说在不阅读 Scott Meyers 和/或观看 YouTube 上的相关谈话的情况下积极使用。

不仅仅是 && 可以是右值引用或“通用引用”,它还是模板用于区分引用类型的方式。想象一下,您是一名从事软件开发的普通工程师。你读到了类似的东西:

template <typename T>
void foo (T&& whatever) {...}

然后 foo 的函数体告诉你输入参数必须是在内部库中定义的非常特定的类类型。太好了,您认为,该模板在当前软件版本中已过时,可能是以前开发的遗留物,所以让我们摆脱它。

祝编译器错误好运……</p>

没有明确了解这一点的人不会假设您在这里将 foo 实现为函数模板,只是为了使用模板推导规则和引用恰好重合的折叠规则,以便您最终得到所谓的“完美转发”。它看起来真的比其他任何东西都更像是一个肮脏的黑客。我认为为通用引用创建语法会使事情变得更简单,并且曾经在旧代码库上工作的开发人员至少会知道这里有一些东西必须用谷歌搜索。只是我的两分钱。

于 2020-12-17T13:10:35.000 回答