如何组合切片,模板和非派生类?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何组合切片,模板和非派生类?相关的知识,希望对你有一定的参考价值。

我想有一个可以摄取对象的容器,并将它的不同部分存储到不同的内部容器中。

像这样的东西:

// Pseudo-code

template<typename A, typename B, typename C>
class Container {
   std::vector<B> vb;
   std::vector<C> vc;
public:
   void push_back(const A &a) {
      vb.push_back(a);
      vc.push_back(a);
   }
};

class AA {
   int a;
   std::string s;
};

class BB {
   int a;
};

class CC {
   std::string s;
};

Container<AA, BB, CC> cont;

AA aa;
cont.push_back(aa);

我想对象vb来获得类A和B(切片)之间的共同点,以及对象vc的相同内容。

理想情况下,AA,BB和CC类不应处于层次关系。我希望编译器能够按类型匹配成员。

我宁愿选择性能较低的解决方案。

答案

您的伪代码容器已经在AA继承BBCC的情况下工作,因为切片允许从派生到基础的隐式转换。但是,您的实施不仅限于此类情况。所需要的只是AA可以隐式转换为BBCC。继承就足够了,但不是必需的。

您可以通过定义转换运算符使类可转换为其他类:

struct AA {
   operator BB() const { return {a}; }
   operator CC() const { return {s}; }
   int a;
   std::string s;
};

或者,您可以将转换构造函数添加到BB和/或CC,如果您希望依赖性转向另一种方式:

struct BB {
   BB(const AA& aa) : a(aa.a) {}
   int a;
};

但请注意,缩小隐式转换(例如这样做)可能会导致某些错误被忽视。

另一答案

您可以使用标签分派来对类型进行分类并有条​​件地选择其成员。一个例子:

template<class T> typename std::is_same<decltype(T::s), std::string>::type test_has_string_s(int);
template<class T> std::false_type test_has_string_s(...);
template<class T> using HasStringS = decltype(test_has_string_s<T>(0));

template<class T> typename std::is_same<decltype(T::a), int>::type test_has_int_a(int);
template<class T> std::false_type test_has_int_a(...);
template<class T> using HasIntA = decltype(test_has_int_a<T>(0));

template<typename B, typename C>
class Container {
    std::vector<B> vb;
    std::vector<C> vc;

    template<class T> void push_back_vb(T const& t, std::true_type) { vb.push_back(B{t.s}); }
    template<class T> void push_back_vb(T const&, std::false_type) {}

    template<class T> void push_back_vc(T const& t, std::true_type) { vc.push_back(C{t.a}); }
    template<class T> void push_back_vc(T const&, std::false_type) {}

public:
    template<class T>
    void push_back(T const& t) {
        static_assert(HasStringS<T>::value || HasIntA<T>::value, "Must have any of {T::a, T::s}.");
        push_back_vb(t, HasStringS<T>{});
        push_back_vc(t, HasIntA<T>{});
    }
};

struct AA {
   int a;
   std::string s;
};

struct BB {
   std::string s;
};

struct CC {
   int a;
};

struct DD {};

int main() {
    Container<BB, CC> cont;
    cont.push_back(AA{});
    cont.push_back(BB{});
    cont.push_back(CC{});
    cont.push_back(DD{}); // error: static assertion failed: Must have any of {T::a, T::s}.
}

以上是关于如何组合切片,模板和非派生类?的主要内容,如果未能解决你的问题,请参考以下文章

如何在其派生类中覆盖列表的切片功能

如何派生抽象模板类,模板类型作为函数参数 (C++11)

如何在派生类中可移植地初始化继承的模板化 POD 结构?

如何使用 C++ 中的模板编程从基类创建派生类?

使用模板实现派生类

如何使用 Boost.serialize 序列化派生模板类?