创建可相互转换的模板类系列

时间:2019-03-31 15:23:43

标签: c++ c++17

我想创建一个模板类系列,每个模板类都将从共同的基础派生。这个共同的基础将定义如何将这些派生类之一的实例转换为任何其他类。我已经使用复制构造函数和复制赋值运算符创建了基类模板,这是代码:

template < class T >
struct class_family
{
    T data;
    class_family() = default;
    class_family(const class_family& a) : data(a.data) { }
    class_family& operator=(const class_family& a) { data = a.data; return *this; };
};

然后,我创建了2个派生类,这些派生类均从该基类派生,因此我可以避免为派生类的每种可能组合复制用于副本构造和副本分配的代码:

template < class T >
struct class_1
    : public class_family< T >
{
    using class_family< T >::class_family;
    using class_family< T >::operator=;
};

template < class T >
struct class_2
    : public class_family< T >
{
    using class_family< T >::class_family;
    using class_family< T >::operator=;
};

这是我的测试代码:

int main(int argc, char** argv)
{
    class_1<int> c1;
    class_2<int> c2;
    c1 = c2;
    // class_2<int> c3 = c1;
}

在进行复制分配时,一切都按预期工作,但是当我取消注释行尝试用类型为class_2的对象初始化类型为class_1的新对象时,编译器会抱怨以下错误:

E0312   no suitable user-defined conversion from "class_1<int>" to "class_2<int>" exists

编译器看不到基类的继承副本构造函数吗?有什么解决方法可以避免为属于class_family的每个单个类复制副本构造函数?

1 个答案:

答案 0 :(得分:3)

更短的繁殖:

struct B { };
struct D : B { using B::B; };
struct E : B { using B::B; };

E e{D{}}; // error

基本上,复制和移动构造函数从不继承-专门考虑将其排除在候选对象之外。如果需要,您必须显式添加这些额外的候选项。

来自[over.match.funcs]/8

  

从类类型C([class.inhctor.init])继承的构造函数,其第一个参数类型为“对 cv1 P的引用”(包括此类如果构造参数列表只有一个参数且引用为D,则构造类型为 cv2 C的对象时,将从候选函数集中排除从模板实例化的构造函数)与P相关,并且PD相关。 [示例:

struct A {
  A();
  A(A &&);                              // #1
  template<typename T> A(T &&);         // #2
};
struct B : A {
  using A::A;
  B(const B &);                         // #3
  B(B &&) = default;                    // #4, implicitly deleted

  struct X { X(X &&) = delete; } x;
};
extern B b1;
B b2 = static_cast<B&&>(b1);            // calls #3: #1, #2, and #4 are not viable
struct C { operator B&&(); };
B b3 = C();                             // calls #3
     

最终示例]


这是CWG 2356,也是CWG 1959P0136也已部分解决)。