使用"using"关键字继承基类的复制和移动构造函数

3

我想使用 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;
}

但我不想重新实现复制和移动构造函数。


Pretty_Function 不是标准写法。 - Jive Dadson
3
@JiveDadson,但它仍然很漂亮。 - anton_rh
这是不可能的。复制构造函数既不能作为复制构造函数被继承,也不能作为转换构造函数被继承。 - n. m.
1
因为标准规定如此。如果从基类引入到派生类的构造函数或赋值运算符具有派生类的复制/移动构造函数或赋值运算符(15.8)的签名,则使用声明本身不会抑制派生类成员的隐式声明;来自基类的成员被派生类的隐式声明的复制/移动构造函数或赋值运算符所隐藏或覆盖。 - n. m.
1
抱歉,这个解释是错误的!它不适用于“具有派生类的复制/移动构造函数或赋值运算符的签名”。它可以适用于A::A(const B&),但对于这种情况不起作用。正确的解释在这里。请注意,它仅适用于构造函数,而不适用于赋值运算符。 - n. m.
显示剩余6条评论
1个回答

2
您需要一个以A为参数的B构造函数。然后您需要将默认构造函数设置为显式。
struct B : A
{
    using A::A;
    using A::operator=;

    B() = default;
    B(const A& a) : A(a) {}
    B(A &&a): A(std::move(a)) {}
};

1
我不需要添加默认构造函数。它可以在没有它的情况下工作(http://coliru.stacked-crooked.com/a/bf8d566e8575fbe6)。我试图避免从`A`到`B`添加构造函数。 - anton_rh
在添加所需的一行代码 B(const A& a) : A(a) {} 后,您确实需要将默认构造函数显式化。接受或离开。 - Jive Dadson
我不知道。我只是给你展示了一个例子,它在没有 B() = default; 的情况下编译良好。但也许这是 gcc 的扩展。 - anton_rh
无论如何,你都有答案。祝你好运。 - Jive Dadson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接