为什么基类中的复制和交换会导致复制分配运算符在派生类中被隐式删除?

仅在GCCClang中进行了测试,基类中存在传递值复制赋值运算符(在实现复制和交换(或复制和- move)习惯用法)导致隐式删除派生类中的副本分配运算符。

Clang和GCC对此表示同意; 为什么会这样?

示例代码:

#include <string>
#include <iostream>

struct base {
    base() {
        std::cout << "no-arg constructor\n";
    }
    base(const base& other) :
        str{other.str} {
        std::cout << "copy constructor\n";
    }
    base(base&& other) :
        str{std::move(other.str)} {
        std::cout << "move constructor\n";
    }
    base& operator=(base other) {
        std::cout << "copy assigment\n";
        str = std::move(other.str);
        return *this;
    }
    base& operator=(base&& other) {
        std::cout << "move assigment\n";
        str = std::move(other.str);
        return *this;
    }

    std::string str;
};

struct derived : base {
    derived() = default;
    derived(derived&&) = default;
    derived(const derived&) = default;
    derived& operator=(derived&&) = default;
    derived& operator=(const derived&) = default;
};

derived foo() {
    derived ret;
    ret.str = "Hello,world!";
    return ret;
}

int main(int argc,const char* const* argv) {

    derived a;
    a.str = "Wat";
    a = foo(); // foo() returns a temporary - should call move constructor
    return 0;
}
lglzn 回答:为什么基类中的复制和交换会导致复制分配运算符在派生类中被隐式删除?

在您的代码中,不会删除派生的副本分配。由于[class.copy.assign]/7.4,因此删除的是移动分配,它指出如果基类上的移动分配的重载解析度不明确,则会删除默认的移动分配运算符。

编译器将无法分辨是调用operator=(base)还是operator=(base&&)来移动基类。


这总是一个问题,即使您尝试移动将一个基类对象直接分配给另一个基类对象。因此,同时具有两个重载是不实际的。我尚不清楚为什么您同时需要两者。据我所知,您可以消除operator=(base&&)重载而不会产生不良影响。

,
  

[class.copy.assign] / 7 如果X具有以下条件,则将类别X的默认复制/移动赋值运算符定义为已删除:
  (7.4)-...无法复制/移动的直接基类M,因为用于找到M的对应赋值运算符的重载解析(16.3)导致歧义。

base的移动分配不明确;它有两个赋值运算符都接受一个右值。请注意,此doesn't compile

base a,b;
a = std::move(b);

因此,derived的移动分配最终定义为已删除。

本文链接:https://www.f2er.com/2882392.html

大家都在问