[英]Partial template specialization for type
I have a class vec_base
defined like so: 我有一个像这样定义的类
vec_base
:
template<typename T, std::size_t Size>
class vec_base;
and I would like to specialize it so that 我想把它专门化
vec_base<float, /* any multiple of 4 */>
and 和
vec_base<double, /* any multiple of 2 */>
can have specific members independently as apposed to, say 例如,可以独立地拥有特定成员
vec_base<int, 6>
which would have generic members that I have already defined 哪个会有我已定义的通用成员
I'm having a tough time implementing this because of the lenient size allowed (any multiple of 4 or 2) if it were specifically 2 or 4 I know I could perform full specialization, but that isn't the case :/ 我很难实现这个,因为允许的宽松大小(4或2的任意倍数),如果它特别是2或4我知道我可以执行完全专业化,但事实并非如此:/
How would I go about this? 我该怎么做? Any help at all is appreciated, I always love learning new language techniques!
感谢任何帮助,我总是喜欢学习新的语言技巧!
okay so I have this so far: 好吧所以我到目前为止:
template<std::size_t Size>
struct is_div_by_4{static const bool value = (Size % 4 == 0);};
// have to define because of template requirements
// about not being dependent on other parameters
template<typename T, std::size_t Size, bool is_special>
class vec_base;
template<typename T, std::size_t Size>
class vec_base<T, Size, false>{
// generic implementation
};
teplate<std::size_t Size>
class vec_base<float, Size, is_div_by_4<Size>::value>{
// Special implementation (for sse, it's no secret)
};
but I haven't compiled it yet and I know it won't work, so please don't point that out; 但我还没有编译它,我知道它不会起作用,所以请不要指出这一点; it's just what I have so far incase you thought I was just deferring my own work to SO.
这就是我到目前为止的想法,因为你认为我只是把自己的工作推迟到了。
The most simple technique would be using std::enable_if
and std::same
similar to what you did: 最简单的技术是使用
std::enable_if
和std::same
类似于你所做的:
template<typename T, std::size_t Size, typename U = void>
class vec_base { /* implement me generically */ };
template<typename T, std::size_t Size>
class vec_base<T, Size, typename std::enable_if<std::is_same<T, float>::value && Size % 4 == 0>::type>
{ /* implement me with 10% more awesome-sauce */ };
template<typename T, std::size_t Size>
class vec_base<T, Size, typename std::enable_if<std::is_same<T, double>::value && Size % 2 == 0>::type>
{ /* implement me with pepper instead */ };
typename U = void
can be avoided typename U = void
The idea behind std::enable if
is something called the SFINAE principle, which basically states that whenever instantiating a template does not work, the compiler will not error out, but instead just remove that one definition from all overload sets and similar name resolutions. std::enable if
背后的想法称为SFINAE原则,它基本上表明无论何时实例化模板都不起作用,编译器都不会出错,而只是从所有重载集和类似的名称解析中删除一个定义。
The implementation behind std::enable_if
specializes the class template, so that std::enable_if<false>
does not contain a member type
at all. std::enable_if
背后的实现专门化了类模板,因此std::enable_if<false>
根本不包含成员type
。 Therefore using that type member will cause an error that (due to SFINAE) removes this specialization from consideration. 因此,使用该类型成员将导致(由于SFINAE)从考虑中删除此特化的错误。
Since your template already contains a type parameter, you could instead use that type parameter, since the std::enable_if<true>::type
is actually the same as its second parameter, a type parameter that only defaults to void
, but can of course be set. 由于您的模板已经包含一个类型参数,您可以改为使用该类型参数,因为
std::enable_if<true>::type
实际上与它的第二个参数相同,一个类型参数只默认为void
,但是当然要设定。
Therefore, you can remove the last template parameter in the generic implementation completely and instead specialize like so: 因此,您可以完全删除泛型实现中的最后一个模板参数,而不是像这样专门化:
template<typename T, std::size_t Size>
class vec_base<typename std::enable_if<std::is_same<T, float>::value && Size % 4 == 0, float>::type, Size>
{ /* implement me with 10% more awesome-sauce */ };
typename T
and std::same
are not necessary typename T
和std::same
不是必需的 From this you can also see that you could remove the typename T
of your specializations and drop the usage of std::is_same
. 从这里你还可以看到你可以删除你的特化的
typename T
并删除std::is_same
的用法。 T
must always be a specific type after all... 毕竟
T
必须始终是特定的类型......
template<std::size_t Size>
class vec_base<typename std::enable_if<Size % 4 == 0, float>::type, Size>
{
friend vec_base operator+(vec_base const& lhs, vec_base const& rhs)
{ /* do some additions */
return lhs;
}
};
Adding more operators outside of the class is fairly simple: 在类之外添加更多运算符非常简单:
// works for all vec_base variants
template<typename T, std::size_t Size>
vec_base<T, Size> operator-(vec_base<T, Size> const& lhs, vec_base<T, Size> const& rhs)
{ /* do some subtractions */
return lhs;
}
// works only for the specialization float, divisible by 4
template<std::size_t Size>
typename std::enable_if<Size % 4 == 0, vec_base<float, Size>>::type
operator-(vec_base<float, Size> const& lhs, vec_base<float, Size> const& rhs)
{ /* do some salty computations */
return lhs;
}
This actually works because the second version is strictly more restricted than the first version (every argument group that works with the special function also works for the generic one - but the generic one has some for which the special one will not work). 这实际上是有效的,因为第二个版本严格比第一个版本更受限制(与特殊函数一起使用的每个参数组也适用于通用版本 - 但是通用版本具有一些特殊版本将不起作用的版本)。
Although you seem rather down about your attempt, here is how to adapt it to work as well (note that this solution is far more convoluted than the one shown above): 虽然你似乎对你的尝试感到沮丧,但这里也是如何使它适应工作(请注意,这个解决方案比上面显示的解决方案更复杂):
template<typename T, std::size_t Size, int mode =
(std::is_same<T, float>::value && Size % 4 == 0) ? 1
: (std::is_same<T, double>::value && Size % 2 == 0) ? 2
: 0>
struct vec_base;
template<typename T, std::size_t Size>
struct vec_base<T, Size, 0>
{ static void hello() { ::std::cout << "hello all\n"; } };
template<std::size_t Size>
struct vec_base<float, Size, 1>
{ static void hello() { ::std::cout << "hello 4 floats\n"; } };
template<std::size_t Size>
struct vec_base<double, Size, 2>
{ static void hello() { ::std::cout << "hello 2 doubles\n"; } };
You would call it like so: 你会这样称呼它:
vec_base<float, 2>::hello(); // hello all
vec_base<float, 4>::hello(); // hello 4 floats
Simpler yet: 更简单:
template<typename T, std::size_t Size>
class vec_base
{
public: vec_base() {
std::cout << "I am generic, my size is " << Size << std::endl;
}
};
template<std::size_t Size>
class vec_base<typename std::enable_if<Size % 4 == 0, float>::type, Size>
{
public: vec_base() {
std::cout << "I am specialized for float, my size is " << Size << std::endl;
}
};
template<std::size_t Size>
class vec_base<typename std::enable_if<Size % 2 == 0, double>::type, Size>
{
public: vec_base() {
std::cout << "I am specialized for double, my size is " << Size << std::endl;
}
};
I would go for a is_special<T, N>
type trait that gives std::false_type
for all types that do not fit into your SSE solution, and std::true_type
otherwise. 我会选择一个
is_special<T, N>
类型特征,它为所有不适合您的SSE解决方案的类型提供std::false_type
,否则为std::true_type
。
#include <cstddef>
#include <iostream>
#include <type_traits>
// test whether a vector of floating points fits into B bits SSE solution
template<typename T, std::size_t N, std::size_t B = 128>
struct is_special
:
std::integral_constant<bool,
std::is_floating_point<T>::value && 8 * sizeof(T) * N == B
>
{};
I generally don't like putting raw conditions in my template specializations that make use of such traits, as this can lead to less maintainable code (especially if you re-use the type trait anywhere else). 我通常不喜欢在我的模板特化中使用原始条件来使用这些特性,因为这会导致代码的可维护性降低(特别是如果你在其他任何地方重用类型特征)。
enable_if
enable_if
专门化您的模板 You can then do a std::enable_if
on this type trait 然后,您可以在此类型特征上执行
std::enable_if
template<typename, std::size_t, typename = void>
class vec_base
{
public:
static void print() { std::cout << "vec_base<T, N>\n"; };
};
template<std::size_t N>
class vec_base<float, N, typename std::enable_if<is_special<float, N>::value>::type>
{
public:
static void print() { std::cout << "vec_base<float, 4 * K>\n"; };
};
template<std::size_t N>
class vec_base<double, N, typename std::enable_if<is_special<double, N>::value>::type>
{
public:
static void print() { std::cout << "vec_base<double, 2 * K>\n"; };
};
Note that the generic template does not need to know about the kind of special conditions that you use for the specializations. 请注意,通用模板不需要知道您用于特化的特殊条件的类型。 This is a manifestion of the Open/Closed principle: your code is closed for modification, but open for extensions.
这是开放/封闭原则的一个表现:您的代码已关闭以进行修改,但对于扩展而言是开放的。 For 128 bit long doubles, just add a new specialization.
对于128位长的双打,只需添加一个新的专业化。
You can call this code like this: 您可以像这样调用此代码:
int main()
{
vec_base<int, 6>::print(); // vec_base<T, N>
vec_base<float, 1>::print(); // vec_base<T, N>
vec_base<float, 4>::print(); // vec_base<float, 4 * K>
vec_base<double, 1>::print(); // vec_base<T, N>
vec_base<double, 2>::print(); // vec_base<double, 2 * K>
}
Live Example that prints the commented lines above. 实例打印上面的注释行。 It's important to test both the cases you want and the cases you don't want to call SSE code.
测试您想要的案例和您不想调用SSE代码的案例非常重要。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.