C++ 模板类型定义?
在外部API中,我定义了结构:C++ 模板类型定义?,c++,templates,specialization,C++,Templates,Specialization,在外部API中,我定义了结构: Foo1,Foo4,Foo8,Foo16 现在我需要定义四个函数: void bar(Foo1*); void bar(Foo4*); void bar(Foo8*); void bar(Foo16*); 这些函数在循环中重复1、4、8和16次执行相同的操作 为了避免编写这些函数4次,我很乐意使用一个模板来定义它们: template<unsigned int N> void bar(Foo<N> * foo) { for(uns
Foo1
,Foo4
,Foo8
,Foo16
现在我需要定义四个函数:
void bar(Foo1*);
void bar(Foo4*);
void bar(Foo8*);
void bar(Foo16*);
这些函数在循环中重复1、4、8和16次执行相同的操作
为了避免编写这些函数4次,我很乐意使用一个模板来定义它们:
template<unsigned int N> void bar(Foo<N> * foo)
{
for(unsigned int i=0;i<N;++i)
{
//Some critical code that optimizes nicely with SSE, AVX, etc...
}
}
这在功能上与我想要实现的功能相同,但在某种程度上增加了bar
code,它将充满foo.f
s,并且依赖于从FooN*
到foo*
的转换
我不知道如何定义模板类Foo,使其专用于Foo1、Foo4、Foo8、Foo16
像这样:
template <int N> struct Foo_impl {};
template <> struct Foo_impl<1 > {using type = Foo1 ;};
template <> struct Foo_impl<4 > {using type = Foo4 ;};
template <> struct Foo_impl<8 > {using type = Foo8 ;};
template <> struct Foo_impl<16> {using type = Foo16;};
template <int N> using Foo = typename Foo_impl<N>::type;
在这里,
throw“Invalid T.”
实际上并不会在运行时抛出,但如果T
不是Foo#
中的一个,则会导致编译时错误。对于运行循环,根据类的不同,可以使用从C++11开始的常量表达式:
template<typename T>
void bar(T) {
constexpr unsigned int N = std::is_same<T, Foo4>::value * 4 + std::is_same<T, Foo16>::value * 16; // likewise for Foo1, Foo8
// ...
}
将这四个类放在一起如下所示:
template <typename T> void bar(T *foo)
{
constexpr int N =
std::is_same_v<T, Foo1 > ? 1 :
std::is_same_v<T, Foo4 > ? 4 :
std::is_same_v<T, Foo8 > ? 8 :
std::is_same_v<T, Foo16> ? 16 : throw "Invalid T.";
// ...
}
template<typename T>
void bar(T *a) {
constexpr unsigned int N = std::is_same<T, Foo1>::value * 1
+ std::is_same<T, Foo4>::value * 4
+ std::is_same<T, Foo8>::value * 8
+ std::is_same<T, Foo16>::value * 16;
static_assert(N > 0, "bar() called with object of invalid type");
for( unsigned int i = 0; i < N; ++i) {
std::cout << i << std::endl;
}
}
这将使用返回类型使函数仅在使用允许的类型(Foo4或Foo16)实例化时编译。从C++17开始,您也可以使用。这样的东西怎么样?专门化函数,而不是类型本身:
template<typename FooT, unsigned N>
void bar_impl(FooT f)
{
for (unsigned int i = 0; i < N; ++i)
{
//Do magic!
}
}
template<typename FooT>
void bar(FooT f);
template<>
void bar<Foo1>(Foo1 f)
{
bar_impl<Foo1, 1>(f);
}
template<>
void bar<Foo4>(Foo4 f)
{
bar_impl<Foo4, 4>(f);
}
template<>
void bar<Foo8>(Foo8 f)
{
bar_impl<Foo8, 8>(f);
}
template<>
void bar<Foo16>(Foo16 f)
{
bar_impl<Foo16, 16>(f);
}
模板
空心钢筋(英尺f)
{
for(无符号整数i=0;i
您可以使用一个helper结构bar\u trait
,您专门为每种类型Foo1
,Foo4
。然后,这些专门化可以保存各种值,您可以在bar
函数中使用这些值来控制代码流
这样,bar
就可以接受您为其创建了bar\u trait
专门化并支持您在bar
中对其应用的表达式的任何类型
这样做的好处是:
- 可以在编译时进行计算,并且不会出现运行时错误
- 您可以专门化
,而无需触摸bar\u trait
bar
- 您可以在其他可能需要知道
大小的地方重复使用FooN
,从而减少代码重复bar\u trait
- 对marcos没有要求
- 模板参数演绎很有效
structfoo1{};
结构Foo4{};
结构Foo8{};
结构Foo16{};
//为bar_trait创建声明,但不创建定义,以便需要专门化
模板
结构条性状;
//为'Foo1','Foo4',创建专门化…
模板
结构条特征{
静态constexpr const size\u t size=1;
};
模板
结构条特征{
静态constexpr const size\u t size=4;
};
模板
结构条特征{
静态constexpr const size\u t size=8;
};
模板
结构条特征{
静态constexpr const size\u t size=16;
};
//只要存在“bar_trait”的专门化,就接受T的任何类型
//应用于“foo”的表达式是有效的
模板空心条(T*foo)
{
constexpr const auto N=bar\u trait::size;
对于(unsigned int i=0;i您可以声明以下方便类模板,Foo\u number\u taker
:
template<typename> struct Foo_number_taker;
您可以很容易地将上面的Foo\u number\u taker
类模板专门化为Foo1
、Foo4
、Foo8
和Foo16
:
DEF_FOO_NUMBER_TAKER(1)
DEF_FOO_NUMBER_TAKER(4)
DEF_FOO_NUMBER_TAKER(8)
DEF_FOO_NUMBER_TAKER(16)
例如,DEF_FOO_NUMBER_TAKER(8)
将扩展到:
template<> struct Foo_number_taker<Foo8> { static constexpr unsigned value = 8; };
最后,定义bar()
函数模板,以利用上述变量模板找出与模板参数FooN
关联的编号:
template<typename FooN>
void bar(FooN* foo) {
auto constexpr N = Foo_number_v<FooN>;
for(unsigned i = 0; i < N; ++i) {
// ...
}
}
感谢所有的答案!我学到了很多东西。
struct Foo1 {};
struct Foo4 {};
struct Foo8 {};
struct Foo16 {};
// create a declaration for bar_trait but no definition so that specializations are required
template<typename T>
struct bar_trait;
// create the specializations for `Foo1`, `Foo4`, …
template<>
struct bar_trait<Foo1> {
static constexpr const size_t size = 1;
};
template<>
struct bar_trait<Foo4> {
static constexpr const size_t size = 4;
};
template<>
struct bar_trait<Foo8> {
static constexpr const size_t size = 8;
};
template<>
struct bar_trait<Foo16> {
static constexpr const size_t size = 16;
};
// accepts any type for T as long as a specialization for `bar_trait<T>` exists
// and the expressions applied on `foo` are valid
template<typename T> void bar(T * foo)
{
constexpr const auto N = bar_trait<T>::size;
for(unsigned int i=0;i<N;++i)
{
//Some critical code that optimizes nicely with SSE, AVX, etc...
}
}
int main()
{
Foo1 foo1;
Foo4 foo4;
bar(&foo1);
bar(&foo4);
}
template<typename> struct Foo_number_taker;
#define DEF_FOO_NUMBER_TAKER(N) template<> \
struct Foo_number_taker<Foo##N> { \
static constexpr unsigned value = N; \
};
DEF_FOO_NUMBER_TAKER(1)
DEF_FOO_NUMBER_TAKER(4)
DEF_FOO_NUMBER_TAKER(8)
DEF_FOO_NUMBER_TAKER(16)
template<> struct Foo_number_taker<Foo8> { static constexpr unsigned value = 8; };
template<typename FooN>
static auto constexpr Foo_number_v = Foo_number_taker<FooN>::value;
template<typename FooN>
void bar(FooN* foo) {
auto constexpr N = Foo_number_v<FooN>;
for(unsigned i = 0; i < N; ++i) {
// ...
}
}
auto main() -> int {
Foo1 *f1;
Foo4 *f4;
// ...
bar(f1);
bar(f4);
}