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


最近,我跟着讨论了C ++中表达式的赋值,如下例所示:

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

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

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

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


11390
2017-10-06 11:25


起源

一个原因是大多数编译器还不支持语法。另一个是它没有解决问题 重大的 问题。错误多久发生一次? - Bo Persson
我认为它确实发生了。 if (somefunc() = value) 当然,大多数编译器会对此发出警告,但并非在所有情况下都会发出警告。 - Tamás Szelei


答案:


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

你没有看到它的原因大多是糟糕的编译器支持 - 右值引用 *this 有点像 thread_local大多数编译器实现者似乎都把它放在“从C ++ 11实现的功能”堆栈的底部。


4
2017-10-06 12:54



在我看来,最好的答案。虽然我对赋值运算符更感兴趣,因为它是一个特殊的成员函数。使用引用限定符可能会对5规则,继承,作为聚合的成员变量或使用标准容器和标准库产生一些影响。但是,我应该一直在询问这些信息。 - nosid


有趣!我甚至都没有意识到这一点并带我去找它(这是它的一部分 “将移动语义扩展到* 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;
};

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

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

#include <vector>
void f(std::vector<int>&);
int main()
{
    f(std::vector<int>() = std::vector<int>(10));
}

这可能是滥用赋值运算符从rvalue获得左值,但不太可能偶然发生。因此,我不会通过限制赋值运算符仅适用于左值来使我的方式变得不可能。当然,将rvalue从赋值返回到右值也会阻止这种情况。如果有的话,这两种用途中哪一种更有用,可能是一个考虑因素。

BTW,clang似乎支持自2.9版以来引用的语法。


8
2017-10-06 12:50



我宁愿说清楚 template<class T> T& as_lvalue(T&& v){ return v; } 比起我的同伴们的思维能力,而不是想知道你正在努力实现什么样的任务(对于移动 - 不知道,也似乎包括一个无用的副本 vector)。 - Xeo


我不是非常热衷于你的建议的一个原因是 我试图回避完全宣布特殊成员。因此,我的大多数赋值运算符都被隐式声明,因此没有ref-qualifiers。

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


3
2017-10-06 14:11



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