C ++:使用参数包的变量类型参数的模板类继承

时间:2016-12-19 13:39:41

标签: c++11 templates inheritance

(我不知道如何命名这个问题而我找不到类似的东西。抱歉,如果这是重复的话)

如果我想继承一些基本模板类,我可以这样做:

template<typename A=int, typename B=char> class C {};
template<typename... Args> class D : public C<Args...> {}; //it works!

这样我可以将项目传递的参数更改为模板类C,而且我不必更改D类的每个用法。很棒。但是如果我有模板类不仅使用类型作为参数而且还使用值?例如:

template<int dim=3, typename float_t=double> class GeometricObject{};
template<typename... Args> class Point : public GeometricObject<Args...>{}; //it doesnt work

当然我可以在开头定义最后一个带整数类型的模板。但这不是一种方法,如果我有100个不同的类都继承自GeometricObject,然后我将默认的dim值更改为2,我将不得不更改每个类的定义。

我也希望不使用任何#define#else和类似的预处理器命令。我知道模板实际上也是预处理器命令,但是......好吧,让我们现代化;)

3 个答案:

答案 0 :(得分:4)

您不能在模板参数包中混合使用type和non-type参数。但似乎您的Point和其他派生类不需要单独访问参数包参数。在这种情况下,传递基类更容易,也更语义正确:

template<int dim=3, typename float_t=double> class GeometricObject{};
template<class GeometricObject=GeometricObject<>> class Point : public GeometricObject{};

实例化Point可能如下所示:

Point<> a{}; // the same as Point<GeometricObject<>> a{};
Point<GeometricObject<4>> b{};
Point<GeometricObject<2, float>> c{};

当然,GeometricObject<...>可以是更短的类型。此外,它可以看起来像命名空间,而不是分别为每个几何对象提供参数:

template<int dim = 3, typename float_t = double>
struct GeometricObjects {
  using Base = GeometricObject<dim, float_t>;
  using Point = ::Point<Base>;
  // ...
};

using TwoDim = GeometricObjects<2>;
TwoDim::Point a{};

答案 1 :(得分:2)

我想你有多个模板类,你希望你的Point对象能够从它们全部继承。

而不是:

template <typename ... Args>
class Point : public GeometricObject<Args...>{};

我会改为:

template <typename T>
class Point : public T {};

现在我们只需定义适当的特征即可在需要时访问类型模板参数。这些类型应该计入std::tuple(例如)。

填补此特征的负担在GeometricObject课程上。例如,根据您的定义,我们将:

template <typename T>
struct type_parameters;

template <int N, typename Float>
struct type_parameters<GeometricObject<N, Float> {
    typedef std::tuple<Float> types;
};

主要方案:Point的方法需要GeometricObject的类型模板参数(将它们转发给GeometricObject的方法)。要实现这一点,您必须传入一个tuple来展开调用内部方法。为此,我使用了STL for C ++ 14中添加的功能。你仍然可以自己重写它们,但我免除了这个问题的麻烦......

template <typename T>
class Point : public T {
    template <typename Method, typename ... Args, std::size_t ... Is>
    auto call_unfold(Method method, std::tuple<Args...> const& tuple, std::integer_sequence<std::size_t, Is...>) {
        return (this->*method)(std::get<Is>(tuple)...);
    }
    template <typename Method, typename Tuple>
    auto call_unfold(Method method, Tuple const& tuple) {
        return call_unfold(method, tuple, std::make_index_sequence<std::tuple_size<Tuple>::value>());
    }
public:
    typedef typename type_parameters<T>::types types;
    void some_method(types const& args) {
        return call_unfold(&T::some_method, args);
    }
};

这个例子没有什么意义,但是同样的技术对于需要调用基类构造函数的Point构造函数很有用。

Coliru

提供了一个展示其工作原理的现场演示

答案 2 :(得分:0)

好的,所以我想出了如何将变量类型的模板参数包含到元组中。基本上我需要将它们“封装”到新参数中。这个例子非常好用并解决了我的问题:

#include <type_traits>

template<int n = 2> struct Dim {
    const int dim = n;
};

template<typename T> class SillyBaseClass {
public:
    typedef typename T dim;
};

template<typename... Args> class SillyDerivedClass : public SillyBaseClass<Args...>{
public:
    typedef typename SillyBaseClass::dim dim;
    SillyDerivedClass() {
        static_assert(std::is_same<dim,Dim<2>>::value,
            "Number of dimensions must be equal to 2");
    }
};

int main() {

    SillyDerivedClass<Dim<2>> Class2d;    //this works
    SillyDerivedClass<Dim<3>> Class3d;    //this returns expected error

}