我正在尝试创建一个基类,它是 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.
发布评论
评论(2)
如果您使用 std::integral_constant 代替 size_t 会怎么样?您可以将数组大小的数值嵌入其中,并且可以将其用作类型。
编辑
为了减少冗长,您可以定义自己的积分常量类,例如:
然后您可以像这样使用它:
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:
Then you could use it like this:
您需要做的是拥有一个
traits
类,专门针对每种类型,其中包含类型升级所需的任何内容,然后将完整类型传递给 StaticValArray。此外,使用 decltype,您不需要这样的东西 - decltype 会告诉您通过添加 float 和 int 会得到什么。
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.