我应该为赋值运算符使用左值引用限定符吗?

Posted

技术标签:

【中文标题】我应该为赋值运算符使用左值引用限定符吗?【英文标题】:Should I use lvalue reference qualifiers for assignment operators? 【发布时间】:2012-09-27 09:10:42 【问题描述】:

最近,我关注了一个关于 C++ 中表达式赋值的讨论,如下例所示:

string s1, s2, s3;
(s1 + s2) = s3;

使用 C++11,可以将赋值运算符限制为左值引用(在左侧)。当如下声明赋值运算符时,编译器 Clang 会因为类型不兼容而拒绝代码并显示错误消息。

auto operator=(const string& rhs) & -> string&;
auto operator=(string&& rhs) & -> string&;

我在任何地方都没有看到这个。是否有充分的理由不为赋值运算符使用左值引用限定符(除了在大多数编译器中缺少支持)?

【问题讨论】:

一个原因是大多数编译器还不支持该语法。另一个是它不能解决主要问题。这种错误发生的频率如何? 我认为它确实发生了。 if (somefunc() = value) 当然,大多数编译器都会为此发出警告,但并非在所有情况下。 【参考方案1】:

有趣!我什至没有意识到这一点,花了我一段时间才找到它(这是"Extending move semantics to *this" 提案的一部分)。该符号在 8.3.5 [dcl.decl] 第 4 段中定义,以防有人想查看。

无论如何:现在,知道了这个特性,如果调用函数的对象是左值或右值,使用它来重载似乎是最有用的,并且可能表现不同。使用它来限制可以做的事情,例如,赋值的结果似乎没有必要,特别是如果对象实际上恰好是一个左值。例如,您可能希望语法从分配给右值返回一个右值:

struct T 
    auto operator=(T&) & -> T&;
    auto operator=(T&&) & -> T&;
    auto operator=(T&) && -> T;
    auto operator=(T&&) && -> T;
;

这里的目的是允许从分配结果中移动(尽管这是否值得,但我不确定:为什么不首先跳过分配?)。我不认为我会主要使用此功能来限制使用。

就个人而言,我喜欢有时从右值中获取左值的可能性,而赋值运算符通常是实现此目的的一种方式。例如,如果您需要将一个左值传递给一个函数,但您知道您不想对它使用任何东西,您可以使用赋值运算符获取左值:

#include <vector>
void f(std::vector<int>&);
int main()

    f(std::vector<int>() = std::vector<int>(10));

这可能是对赋值运算符从右值中获取左值的滥用,但这不太可能是偶然发生的。因此,我不会特意通过将赋值运算符限制为仅适用于左值来使这成为不可能。当然,将右值从赋值返回到右值也可以防止这种情况发生。如果有的话,这两种用途中哪一种更有用。

顺便说一句,clang 似乎支持您从 2.9 版开始引用的语法。

【讨论】:

我宁愿有一个明确的template&lt;class T&gt; T&amp; as_lvalue(T&amp;&amp; v) return v; 函数,也不愿让我的程序员同事想知道你到底想通过任务实现什么(对于移动-不知道,似乎还包括vector 的无用副本。【参考方案2】:

是否有充分的理由不为赋值运算符使用左值引用限定符(除了在大多数编译器中缺少支持)?

不,不是真的。使用 lvalue 或 rvalue 限定符为 lvalue 或 rvalue 对象构造正确的接口与使用 const 相同,并且应该以相同的方式处理 - 每个函数都应考虑限制。分配给右值实际上没有意义,所以应该禁止。

您没有看到它的原因主要是编译器支持不佳-*this 的右值引用有点像thread_local,大多数编译器实现者似乎把它放在“从 C+ 实现的功能”的底部附近+11" 堆栈。

【讨论】:

在我看来最好的答案。虽然我对赋值运算符更感兴趣,因为它是一个特殊的成员函数。使用引用限定符可能会对 5 规则、继承、作为聚合的成员变量或与标准容器和标准库一起使用产生一些影响。但是,我应该一直要求提供这些信息。 请仔细阅读此答案。如果您只阅读问题的标题,您可能会认为“不,不是真的”回答了问题标题。它回答了问题“是否有充分的理由不将左值引用限定符用于赋值运算符?”结尾处的问题? 另请注意,此答案中提到的编译器支持自编写以来已大大改善。自 2019 年起,每个主要和几乎每个次要编译器都支持引用限定符。【参考方案3】:

我对你的建议不是超级热心的一个原因是I'm trying to shy away from declaring special members altogether。因此,我的大多数赋值运算符都是隐式声明的,因此没有引用限定符。

当然,对于那些我确实编写类或类模板的时候,例如管理所有权(参见上面链接中的结论),我可以注意只为左值声明这些运算符。但是,由于它对客户没有影响,因此没有多大意义。

【讨论】:

我同意你的看法。如果可能,我会避免声明 5 个中的任何一个。但是,编译器生成的赋值运算符不限于左值引用。最有可能向后兼容。

以上是关于我应该为赋值运算符使用左值引用限定符吗?的主要内容,如果未能解决你的问题,请参考以下文章

cpp►C++11右值引用移动语义移动构造函数移动赋值运算符

cpp►C++11右值引用移动语义移动构造函数移动赋值运算符

何时重载按引用传递(左值和右值)优于按值传递?

隐式生成的赋值运算符应该是 & ref 限定的吗?

认识左值与常引用

条件运算符中的“错误:需要左值作为赋值的左操作数”