【问题标题】:Variadic Template Template可变参数模板 模板
【发布时间】:2011-04-15 01:21:02
【问题描述】:

我正在尝试创建一个基类,它是 std::array 的包装器,它重载了一堆常见的算术运算符。最终结果将有点像 std::valarray,但具有静态大小。我这样做是因为我正在为我的库创建大量子类,这些子类最终会复制此功能。例如,我需要创建一个 MyPixel 类和一个 MyPoint 类,它们本质上都是静态大小的数组,我可以对其进行算术运算。

我的解决方案是创建一个可以派生 MyPoint 和 MyPixel 的 StaticValArray 基类。但是,为了禁止用户将 MyPoint 添加到 MyPixel,我正在使用 CRTP 模式:

template<class T1, class T2>
struct promote
{
  typedef T1 type; // Assume there is a useful type promotion mechanism here
};

template<class T, size_t S, template<typename... A> class ChildClass>
class StaticValArray : public std::array<T,S>
{
  public:
    // Assume there are some conversion, etc. constructors here...

    template<class U>
    StaticValArray<typename promote<T,U>::type,S,ChildClass> operator+ 
        (StaticValArray<U,S,ChildClass> const & rhs)
    {
      StaticValArray<typename promote<T,U>::type,S,ChildClass> ret = *this;
      std::transform(this->begin(), this->end(),
          rhs.begin(), ret.begin(), std::plus<typename promote<T,U>::type>());
      return ret;
    }


    // More operators....
};

这很酷,因为 ChildClass 可以有任意的类模板参数,这个东西可以工作。例如:

template<class T, class U>
class MyClassTwoTypes : public StaticValArray<T,3,MyClassTwoTypes>
{ };

template<class T, class U>
class MyClassTwoTypes2 : public StaticValArray<T,3,MyClassTwoTypes2>
{ };

int main()
{
  MyClassTwoTypes<int, float> p;
  MyClassTwoTypes<double, char> q;
  auto z = p + q;

  MyClassTwoTypes2<double, char> r;
  //  r += q;  // <-- Great! This correctly won't compile

  return 0;
}

我的问题是:我想将一些 ChildClass 填充到 StaticValArray 的 CRTP 位中,它不一定只有类作为其模板参数。例如,考虑这个 N 维点类:

template<class T, size_t S>
class MyPointND : public StaticValArray<T,S,MyPointND>
{ };

不幸的是,这不会编译,因为 size_t 不是类型名 - 我得到编译器错误:

type/value mismatch at argument 3 in template parameter list for ‘template<class T, long unsigned int S, template<class ... A> class ChildClass> class StaticValArray’
test.C:36:54: error:   expected a template of type ‘template<class ... A> class ChildClass’, got ‘template<class T, long unsigned int S> class MyPointND’

有没有办法创建一个可变参数模板模板参数包,它可以是任何东西(类型名、整数、size_t、双精度数等等?),因为最后我真的不在乎里面的类型是什么。请注意,我不能只完全指定 ChildClass(例如 class MyPointND: public StaticValArray&lt;T,S,MyPointND&lt;T,S&gt;&gt;),因为这会破坏我的类型提升机制。

【问题讨论】:

  • 关于promote结构,你可以在结构中利用decltype:typedef decltype(T+U) type;
  • 我真正的推广实现确实使用了这个,为了清楚起见,我只是称之为“推广”。为简洁起见,此处省略了详细信息。

标签: c++ templates c++11


【解决方案1】:

如果您使用 std::integral_constant 代替 size_t 会怎样?您可以将数组大小的数值嵌入其中,并且可以将其用作类型。

编辑

为了减少冗长,您可以定义自己的整数常量类,例如:

template <std::size_t N>
struct size_ : std::integral_constant<std::size_t,N> {};

那么你可以这样使用它:

MyPointND<int,size_<3>> x;

【讨论】:

  • 嗯,这似乎是在正确的方向!不幸的是,MyPointND 的实例化有点啰嗦:MyPointND> x;
  • 你可以做的是创建你自己的类派生自 std::integral (上面编辑过的帖子)。
【解决方案2】:

您需要做的是拥有一个traits 类,专门用于包含类型提升所需的任何内容的每种类型,然后将完整类型传递给 StaticValArray。

此外,使用decltype,您不需要这样的东西-decltype 会告诉您通过添加浮点数和整数得到什么。

template<class U>
StaticValArray<decltype(*(T*)nullptr + *(U*)nullptr),S,ChildClass> operator+ 
    (StaticValArray<U,S,ChildClass> const & rhs)
{
  StaticValArray<decltype(*(T*)nullptr + *(U*)nullptr),S,ChildClass> ret = *this;
  std::transform(this->begin(), this->end(),
      rhs.begin(), ret.begin(), std::plus<decltype(*(T*)nullptr + *(U*)nullptr)>());
  return ret;
}

【讨论】:

  • 抱歉,我一定遗漏了一些东西——你能详细说明一下traits类吗?
  • DeadMG: 用 std::declval 代替转换和取消引用 nullptr 不合适吗?
猜你喜欢
  • 2016-12-01
  • 2012-03-28
  • 1970-01-01
  • 2013-09-14
  • 1970-01-01
  • 2020-04-29
  • 2014-09-08
  • 2014-04-12
  • 1970-01-01
相关资源
最近更新 更多