【问题标题】:C++: template class inheritance with variable-type parameters using parameter packsC++:模板类继承与使用参数包的可变类型参数
【发布时间】:2016-12-19 13:39:41
【问题描述】:

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

如果我想从某个基模板类继承,我可以这样做:

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 和类似预处理器命令的方法。我知道模板实际上也是预处理器命令,但是......好吧,让我们在这里变得现代;)

【问题讨论】:

  • 你的值参数是和类型参数分开的吗,即值参数总是像这样template&lt;int x=3, int y=2, typename X = int, typename Y=double&gt; ?
  • 有可能。如果我不知道一般答案,那么具体答案也很好:)
  • 你不能直接传递专门的模板并使用适当的typedef访问参数包吗?
  • 请举个例子,我不是很好的模板弯曲者。

标签: c++11 templates inheritance


【解决方案1】:

您不能在模板参数包中混合使用类型和非类型参数。但是您的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&lt;...&gt; 可以被类型定义为更短的内容。此外,它可以看起来像一个命名空间,而不是分别为每个几何对象提供参数:

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{};

【讨论】:

  • 这个o.O有什么魔力我认为using语句是C#的领域。感谢您的回复!
  • using TwoDim = GeometricObjects&lt;2&gt;typedef GeometricObjects&lt;2&gt; TwoDim 相同,只是更“现代”:-)
  • 哪一部分看起来像魔法?我很乐意详细说明。
  • 只是using 语句,就像您使用变量类型一样。这几乎就像在 C++ 中找到类似 python 的智能快捷方式。
  • 感谢您的改进。我改编了随后的文字。这是 SO 上using 语法的链接:stackoverflow.com/questions/10747810/…
【解决方案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,它将展开以调用内部方法。为此,我利用了在 C++14 的 STL 中添加的功能。你仍然可以自己重写它们,但我省去了这个问题的麻烦......

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

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

【讨论】:

  • 非常感谢您的回答。看起来我没有选择,如果我想弄乱模板,我需要编写更复杂的代码。谢谢! :)
【解决方案3】:

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

#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

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-25
    • 2014-07-15
    • 2012-03-13
    • 2016-10-05
    • 1970-01-01
    • 1970-01-01
    • 2015-11-10
    • 2012-05-12
    相关资源
    最近更新 更多