Наследование конструкторов копирования и перемещения базового класса с использованием ключевого слова

Я хочу унаследовать конструктор копирования базового класса, используя ключевое слово using:

#include <iostream>

struct A
{
    A() = default;

    A(const A  &) { std::cerr << __PRETTY_FUNCTION__ << std::endl; }
    A(      A &&) { std::cerr << __PRETTY_FUNCTION__ << std::endl; }

    A& operator=(const A  &) { std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this; }
    A& operator=(      A &&) { std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this; }
};

struct B : A
{
    using A::A;
    using A::operator=;

    B& operator=(const B  &) { std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this; }
    B& operator=(      B &&) { std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this; }
};

int main()
{
    A a;
    B b;
    b = a; // OK
    B b1(          a ); // compile error
    B b2(std::move(a)); // compile error
    return 0;
}

Наследование оператора присваивания с использованием ключевого слова using работает нормально, но наследование конструкторов копирования и перемещения вызывает ошибку компиляции: унаследованный конструктор не является кандидатом на инициализацию из выражения того же или производного типа.

http://coliru.stacked-crooked.com/a/fe84b429c391c894:

main.cpp:16:14: note:   an inherited constructor is not a candidate for initialization from an expression of the same or derived type
main.cpp:8:5: note: candidate: A::A(A&&)
     A(      A &&) { std::cerr << __PRETTY_FUNCTION__ << std::endl; }
     ^
main.cpp:16:14: note:   inherited here
     using A::A;

Почему я могу наследовать оператор присваивания, но не могу наследовать конструктор копирования? Какая разница? Я мог бы понять, если бы я тоже не мог наследовать операторы присваивания. Но наследование операторов присваивания, наоборот, считается нормальным. Для меня это немного странно.

История

То, что я хочу, похоже на то, что задается в этом вопросе: я хочу просто добавить новые методы в существующий класс, не изменяя его (это класс из другой библиотеки).

http://coliru.stacked-crooked.com/a/149a6194717cd465:

#include <iostream>

struct A // not my class
{
};

struct B : A
{
    using A::A;
    using A::operator=;

    void foo() { std::cerr << "fuu" << std::endl; }
};

A NotMyFunc()
{
    return {};
}

int main()
{
    B b(NotMyFunc());
    b.foo();
    return 0;
}

Но я не хочу повторно реализовывать конструкторы копирования и перемещения.


person anton_rh    schedule 01.03.2018    source источник
comment
Pretty_Function не является стандартной.   -  person Jive Dadson    schedule 01.03.2018
comment
@JiveDadson, но все равно красиво.   -  person anton_rh    schedule 01.03.2018
comment
Не в моем компиляторе. Это отсутствует.   -  person Jive Dadson    schedule 01.03.2018
comment
Это невозможно. Конструктор копирования не наследуется ни как конструктор копирования, ни как конструктор преобразования.   -  person n. 1.8e9-where's-my-share m.    schedule 01.03.2018
comment
@ n.m., а почему? Почему я могу наследовать оператор присваивания, но не могу наследовать конструктор копирования / преобразования? Для меня это немного странно.   -  person anton_rh    schedule 01.03.2018
comment
если вы не хотите повторно реализовывать копии и перемещать ctors, не могли бы вы просто делегировать им в производном классе?   -  person solstice333    schedule 01.03.2018
comment
@ solstice333, см. ответ JiveDadson: stackoverflow.com/a/49045417/5447906   -  person anton_rh    schedule 01.03.2018
comment
Потому что так сказано в стандарте. Если конструктор или оператор присваивания, перенесенный из базового класса в производный класс, имеет сигнатуру конструктора копирования / перемещения или оператора присваивания для производного класса (15.8), объявление using само по себе не подавляет неявное объявление производного класса. член класса; член базового класса скрыт или переопределен неявно объявленным конструктором копирования / перемещения или оператором присваивания производного класса.   -  person n. 1.8e9-where's-my-share m.    schedule 01.03.2018
comment
@anton_rh а, хорошо, да, это в значительной степени делегирование. Спасибо   -  person solstice333    schedule 01.03.2018
comment
@ n.m., хорошо, а почему тогда using A::operator=; все еще работает?   -  person anton_rh    schedule 01.03.2018
comment
Извините, это неправильное оправдание! Он не работает, поскольку имеет подпись конструктора копирования / перемещения или оператора присваивания для производного класса. Это сработает для A::A(const B&), но не для этого случая. Правильное обоснование находится здесь. Обратите внимание, что это относится только к конструкторам, а не к операторам присваивания.   -  person n. 1.8e9-where's-my-share m.    schedule 01.03.2018


Ответы (1)


Вам нужен конструктор для B с параметром A. Затем вам нужно сделать конструктор по умолчанию явным.

struct B : A
{
    using A::A;
    using A::operator=;

    B() = default;
    B(const A& a) : A(a) {}
    B(A &&a): A(std::move(a)) {}
};
person Jive Dadson    schedule 01.03.2018
comment
Мне не нужно явно добавлять конструктор по умолчанию. Он работает без него (coliru.stacked-crooked.com/a/bf8d566e8575fbe6). Я стараюсь избегать добавления конструктора из A в B. - person anton_rh; 01.03.2018
comment
Вам нужно сделать конструктор по умолчанию явным после добавления однострочного B(const A& a) : A(a) {}, который является обязательным. Возьми это или оставь. - person Jive Dadson; 01.03.2018
comment
Я не знаю. Я только что показал вам пример того, что он отлично компилируется без B() = default;. Но, возможно, это расширение gcc. - person anton_rh; 01.03.2018
comment
В любом случае ответ у вас есть. Удачи. - person Jive Dadson; 01.03.2018