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

Posted

技术标签:

【中文标题】隐式生成的赋值运算符应该是 & ref 限定的吗?【英文标题】:Should implicitly generated assignment operators be & ref-qualified? 【发布时间】:2013-06-04 10:21:09 【问题描述】:

以下代码在 gcc 4.8.1 上编译没有问题:

#include <utility>

struct foo

;

int main()

    foo bar;

    foo() = bar;
    foo() = std::move( bar );

似乎为foo 隐式生成的赋值运算符不是&amp; ref-qualified,因此可以在右值上调用。根据标准,这是否正确?如果是这样,要求隐式生成的赋值运算符是&amp; ref-qualified的原因是什么?

为什么标准不要求生成以下内容?

struct foo

  foo & operator=( foo const & ) &;

  foo & operator=( foo && ) &;
;

【问题讨论】:

请记住,在 C++11 之前存在隐式复制赋值运算符,因此在非静态成员函数存在引用限定符之前。这和标准委员会在使用更新的编译器编译以前的 C++ 代码时的理念。 @Luc 出于某种原因,直到我发布问题后,我才想到向后兼容。我忘记了典型的成本/收益分析在考虑更改标准时并不适用。 相关:***.com/questions/53007802 【参考方案1】:

好吧,分配给右值有一些合法的用例。引用Ref-qualifiers for assignment operators in the Standard Library:

只有少数非常具体的类型有意义 支持分配给右值。特别是,作为 代理,例如vector::reference,以及赋值的类型 运算符是 const 限定的(例如 slice_array)。

C++ 标准委员会显然认为默认赋值不应该有一个隐含的 ref 限定符——而是应该显式声明。事实上,如果突然间所有隐式声明的赋值运算符都不能使用右值,那么现有的代码可能会停止工作。

诚然,设计一个我们希望隐式声明的赋值运算符与右值一起使用的示例有点困难,但 C++ 标准委员会可能不想在保持向后兼容性方面冒险。像这样的代码:

int foo_counter = 0;

struct Foo

    Foo()
    
        ++foo_counter;
    

    ~Foo()
    
        --foo_counter;
    
;

int main()

    Foo() = Foo();

...不再起作用了。归根结底,标准委员会希望确保以前有效的 C++(无论多么愚蠢或做作)继续在 C++11 中工作。

【讨论】:

似乎他们已经对链接的提案应用了相同的逻辑(re:向后兼容性),因为该问题已作为“不是缺陷”而关闭。所以std::string() = std::string() 的所有实例都将保持有效(无论好坏)。【参考方案2】:

您的问题似乎更多的是“为什么分配给右值会有用?”而不是“为什么标准的 ref 限定自动生成的构造函数? em>"

允许分配给右值的原因是因为在某些情况下它很有用。

一个示例用法是std::tie (link):

#include <set>
#include <tuple>

int main()

    std::set<int> s;

    std::set<int>::iterator iter;
    bool inserted;

    // unpacks the return value of insert into iter and inserted
    std::tie(iter, inserted) = s.insert(7);

从 cppreference.com 借用然后修改的示例

【讨论】:

这并没有真正回答这个问题,因为 OP 专门询问隐式声明的赋值运算符,而不是一般分配给右值 @CharlesSalvia 嗯,也许你是对的。我回答的很匆忙,有点误解了这个问题。你的回答更直接地回答了它。

以上是关于隐式生成的赋值运算符应该是 & ref 限定的吗?的主要内容,如果未能解决你的问题,请参考以下文章

C++ 隐式生成的赋值运算符的异常安全性

拷贝构造函数 & 拷贝赋值运算符

为啥定义了移动构造函数而隐式删除了赋值运算符?

对象的赋值运算符

隐式移动构造函数和赋值运算符

为啥移动赋值运算符应该返回对 *this 的引用 [重复]