首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >通用引用的语法

通用引用的语法
EN

Stack Overflow用户
提问于 2013-01-13 19:07:45
回答 3查看 2.7K关注 0票数 27

这是一个右值引用:

代码语言:javascript
运行
复制
void foo(int&& a);

它不绑定到左值:

代码语言:javascript
运行
复制
int i = 42;
foo(i);   // error

这是一个通用的参考:

代码语言:javascript
运行
复制
template<typename T>
void bar(T&& b);

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

代码语言:javascript
运行
复制
bar(i);   // okay

这是一个右值引用:

代码语言:javascript
运行
复制
template<typename T>
struct X
{
    void baz(T&& c);
};

它不绑定到左值:

代码语言:javascript
运行
复制
X<int> x;
x.baz(i);   // error

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-01-13 22:03:50

通用引用(如T&& )可以将T推断为“对象类型”或“引用类型”。

在您的示例中,当传递一个右值时,它可以将T推导为int,因此函数参数是int&&,或者当传递一个左值时,它可以将T推导为int&,在这种情况下,函数参数是int& (因为引用折叠规则说std::add_rvalue_reference<int&>::type只是int&)

如果T不是通过函数调用推导出来的(就像您的X::baz示例中那样),那么它就不能被推导成int&,所以这个引用不是通用引用。

所以我真的不需要新的语法,它很好地适应了模板参数推导和引用折叠规则,只是做了一些小调整,模板参数可以被推导为引用类型(在C++03中,类型为TT&的函数模板参数总是将T推导为对象类型)。

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

实际上,一个替代的语法实际上会更令人困惑。如果您使用template<typename T> void bar(T&@)作为通用引用的语法,但语义与今天相同,那么在调用bar(i)时,模板参数T可以推导为int&int,而函数参数的类型将为int&int&&……这两个都不是"T&@“(不管它是什么类型)。所以你的语言中有一个声明符T&@的语法,它不是一个可以存在的类型,因为它实际上总是引用其他类型,要么是int&,要么是int&&

至少在我们得到的语法中,类型T&&是一个真实的类型,并且引用折叠规则并不特定于使用通用引用的函数模板,它们与模板之外的类型系统的其余部分完全一致:

代码语言:javascript
运行
复制
struct A {} a;
typedef A& T;
T&& ref = a;    // T&& == A&

或者等效地:

代码语言:javascript
运行
复制
struct A {} a;
typedef A& T;
std::add_rvalue_reference<T>::type ref = a;    // type == A&

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

票数 22
EN

Stack Overflow用户

发布于 2016-12-12 08:52:07

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

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

在“类模板参数演绎”(P0099R3)的C++17特性中可以找到这种混乱的最新方面。在这里,通过转换构造函数和构造函数模板的签名来形成函数模板签名。像这样的东西:

代码语言:javascript
运行
复制
template<typename T> struct S {
  S(T&&);
};

函数模板签名

代码语言:javascript
运行
复制
template<typename T> auto S(T&&)->S<T>;

是用来推导声明的,比如

代码语言:javascript
运行
复制
int i = 42;
S s = i;  // Deduce S<int> or S<int&>?

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

票数 7
EN

Stack Overflow用户

发布于 2020-12-17 21:10:35

作为一名只有几年C++经验的开发人员,我必须承认通用引用是令人困惑的。在我看来,这些东西完全不可能理解,更不用说在没有阅读Scott Meyer和/或在YouTube上观看相关演讲的情况下积极使用了。

这不仅仅是因为&&既可以是r值引用,也可以是“通用引用”,这是模板用于区分引用类型的方式。假设你是一名从事软件开发的普通工程师。你会读到类似这样的东西:

代码语言:javascript
运行
复制
template <typename T>
void foo (T&& whatever) {...}

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

祝你在编译器错误…中好运

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14302849

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档