【问题标题】:Why is copy assigment possible, if a class has only a (templated) move assignment operator?如果一个类只有一个(模板化的)移动赋值运算符,为什么可以进行复制赋值?
【发布时间】:2021-12-13 11:36:45
【问题描述】:

我今天偶然发现了我不明白的代码。请考虑以下示例:

#include <iostream>
#include <string>

class A
{
public:
    template <class Type>
    Type& operator=(Type&& theOther)
    {
        text = std::forward<Type>(theOther).text;

        return *this;
    }

private:
    std::string text;
};

class B
{
public:
    B& operator=(B&& theOther)
    {
        text = std::forward<B>(theOther).text;

        return *this;
    }

private:
    std::string text;
};

int main()
{
    A a1;
    A a2;
    a2 = a1;

    B b1;
    B b2;
    b2 = b1;

    return 0;
}

编译时,MinGW-w64/g++ 10.2 声明:

..\src\Main.cpp: In function 'int main()':
..\src\Main.cpp:41:7: error: use of deleted function 'B& B::operator=(const B&)'
   41 |  b2 = b1;
      |       ^~
..\src\Main.cpp:19:7: note: 'B& B::operator=(const B&)' is implicitly declared as deleted because 'B' declares a move constructor or move assignment operator
   19 | class B
      |       ^
mingw32-make: *** [Makefile:419: Main.o] Error 1

我完全理解错误信息。但我不明白为什么我没有收到与A 类相同的消息。模板化的移动赋值运算符不也是移动赋值运算符吗?那么为什么没有删除复制赋值运算符呢?这是写得很好的代码吗?

【问题讨论】:

    标签: c++ c++11 templates move-assignment-operator


    【解决方案1】:

    模板化的移动赋值运算符不也是移动赋值运算符吗?

    不,它不被视为move assignment operator

    (强调我的)

    类 T 的移动赋值运算符是一个名为 operator=非模板 非静态成员函数,它只接受一个类型为 T&amp;&amp;const T&amp;&amp;、@987654326 的参数@,或const volatile T&amp;&amp;

    因此,A 仍然具有隐式声明的复制/移动赋值运算符。

    顺便说一句:您的模板赋值运算符采用forwarding reference,它可以接受左值和右值。在a2 = a1; 中,它在重载决议中胜过生成的复制赋值运算符并被调用。

    【讨论】:

      【解决方案2】:

      作为@songyuanyao 基于标准的答案的补充:这些语言规则是诸如 MISRA/AUTOSAR(安全关键 C++ 开发的语言指南)等指南具有“避免开发人员混淆”规则的常见原因,例如:

      (来自AUTOSAR C++14 Guidelines

      规则 A14-5-1(必需、实施、自动化)

      模板构造函数不应参与重载决议 对于封闭类类型的单个参数。

      基本原理

      模板构造函数永远不是复制或移动构造函数,并且 因此 不会阻止复制或移动的隐式定义 构造函数,即使模板构造函数看起来相似并且可能 容易混淆。同时,复制或移动操作不 必须只使用复制或移动构造函数,但通过 正常的重载解析过程以找到最佳匹配函数 使用。在以下情况下,这可能会导致混淆:

      • 未选择看起来像复制/移动构造函数的模板构造函数
      • 对于复制/移动操作,因为编译器已生成隐式复制/移动构造函数以及模板构造函数 优先选择复制/移动构造函数,因为 模板构造函数是更好的匹配

      为避免这些令人困惑的情况,模板构造函数不应 参与重载决议的单个参数 封闭类类型以避免选择模板构造函数 用于复制/移动操作。它也清楚地表明了构造函数 不是复制/移动构造函数,并且它不会阻止 隐式生成复制/移动构造函数。

      规则 M14-5-3(必需、实施、自动化)

      当有模板时,应声明复制赋值运算符 带有一个泛型参数的赋值运算符。

      也就是说,对于开发人员来说,模板复制/移动 ctor/赋值运算符不抑制(/不“激活”规则 5)隐式生成的运算符可能会令人惊讶。您通常需要使用 SFINAE 通过允许重载对封闭类。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-11-21
        • 1970-01-01
        • 2020-02-28
        • 1970-01-01
        • 2020-05-16
        • 2014-01-27
        • 1970-01-01
        • 2019-06-30
        相关资源
        最近更新 更多