可变参数模板 模板

发布于 2024-11-01 02:31:26 字数 2522 浏览 0 评论 0 原文

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

我的解决方案是创建一个 StaticValArray 基类,MyPoint 和 MyPixel 可以从中派生。然而,为了禁止用户将 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's,双精度数,等等?)因为最后我真的不在乎里面的类型是什么。请注意,我不能完全指定 ChildClass(例如 class MyPointND: public StaticValArray>),因为这会破坏我的类型提升机制。

I am trying to create a base class that is a wrapper around std::array that overloads a bunch of common arithmetic operators. The end result will be sort of like std::valarray, but with static size. I'm doing this because I am creating a whole host of child classes for my library that end up replicating this functionality. For example, I need to create a MyPixel class and a MyPoint class, both of which are essentially just statically sized arrays that I can perform arithmetic on.

My solution is to create a StaticValArray base class from which MyPoint and MyPixel can derive. However to disallow users from adding a MyPoint to a MyPixel, I'm using the CRTP pattern as such:

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

This is pretty cool, because the ChildClass can have any arbitrary class template parameters, and this thing will work. For example:

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

My problem is this: I would like to stuff some ChildClass into the CRTP bit of StaticValArray that doesn't necessarily have just classes as its template parameters. For example, consider this N-Dimensional Point class:

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

This unfortunately won't compile, because size_t is not a typename - I get the compiler error:

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’

Is there any way to create a variadic template template parameter pack that can be absolutely anything (typenames, ints, size_t's, doubles, whatever?) because in the end I really don't care what the type is in there. Note that I can't just fully specify ChildClass (e.g. class MyPointND: public StaticValArray<T,S,MyPointND<T,S>>) because this would break my type promotion mechanism.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

情徒 2024-11-08 02:31:26

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

编辑

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

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

然后您可以像这样使用它:

MyPointND<int,size_<3>> x;

What if, in place of the size_t, you used an std::integral_constant? You would embed the numerical value of the size of your array in it, and you could use it as a type.

EDIT

In order to reduce the verbosity, you could define your own integral constant class, something like:

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

Then you could use it like this:

MyPointND<int,size_<3>> x;
独行侠 2024-11-08 02:31:26

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

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

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

What you need to do is have a traits class, specialized for each type containing whatever you need for type promotion, and then pass in the complete type to the StaticValArray.

Moreover, with decltype, you shouldn't need anything like this- decltype will tell you what you get by adding a float and an int.

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文