【发布时间】:2010-10-04 16:30:48
【问题描述】:
在声明模板时,我习惯了这种代码:
template <class T>
但是in this question,他们使用了:
template <unsigned int N>
我检查了它是否可以编译。但是这是什么意思?它是非类型参数吗?如果是这样,我们如何才能有一个没有任何类型参数的模板?
【问题讨论】:
在声明模板时,我习惯了这种代码:
template <class T>
但是in this question,他们使用了:
template <unsigned int N>
我检查了它是否可以编译。但是这是什么意思?它是非类型参数吗?如果是这样,我们如何才能有一个没有任何类型参数的模板?
【问题讨论】:
是的,它是一个非类型参数。你可以有几种模板参数
你所拥有的是最后一种。它是一个编译时常量(所谓的常量表达式),并且是整数或枚举类型。在标准中查找后,我不得不将类模板上移到类型部分——即使模板不是类型。但是为了描述这些类型,它们被称为类型参数。您可以拥有指针(以及成员指针)和对具有外部链接的对象/函数的引用(可以从其他目标文件链接到并且其地址在整个程序中是唯一的)。例子:
模板类型参数:
template<typename T>
struct Container {
T t;
};
// pass type "long" as argument.
Container<long> test;
模板整数参数:
template<unsigned int S>
struct Vector {
unsigned char bytes[S];
};
// pass 3 as argument.
Vector<3> test;
模板指针参数(将指针传递给函数)
template<void (*F)()>
struct FunctionWrapper {
static void call_it() { F(); }
};
// pass address of function do_it as argument.
void do_it() { }
FunctionWrapper<&do_it> test;
模板引用参数(传递整数)
template<int &A>
struct SillyExample {
static void do_it() { A = 10; }
};
// pass flag as argument
int flag;
SillyExample<flag> test;
模板模板参数。
template<template<typename T> class AllocatePolicy>
struct Pool {
void allocate(size_t n) {
int *p = AllocatePolicy<int>::allocate(n);
}
};
// pass the template "allocator" as argument.
template<typename T>
struct allocator { static T * allocate(size_t n) { return 0; } };
Pool<allocator> test;
没有任何参数的模板是不可能的。但是没有任何显式参数的模板是可能的 - 它具有默认参数:
template<unsigned int SIZE = 3>
struct Vector {
unsigned char buffer[SIZE];
};
Vector<> test;
在语法上,template<> 保留用于标记显式模板特化,而不是没有参数的模板:
template<>
struct Vector<3> {
// alternative definition for SIZE == 3
};
【讨论】:
SillyExample的代码段不能被GCC 4.8.4编译。第一个错误是the value of ‘flag’ is not usable in a constant expression。还有其他错误
完全可以在整数而不是类型上对类进行模板化。我们可以将模板化的值分配给一个变量,或者以我们可能使用任何其他整数文字的方式对其进行操作:
unsigned int x = N;
事实上,我们可以创建在编译时进行评估的算法(来自Wikipedia):
template <int N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0>
{
enum { value = 1 };
};
// Factorial<4>::value == 24
// Factorial<0>::value == 1
void foo()
{
int x = Factorial<4>::value; // == 24
int y = Factorial<0>::value; // == 1
}
【讨论】:
static constexpr int 类型来代替您的enum。所以Factorial<0> 模板会有static constexpr int value = 1,而template <int N> struct Factorial 可以有static constexpr int value = N * Factorial<N - 1>::value;
constexpr 之前回答的。
您根据“无符号整数”模板化您的类。
例子:
template <unsigned int N>
class MyArray
{
public:
private:
double data[N]; // Use N as the size of the array
};
int main()
{
MyArray<2> a1;
MyArray<2> a2;
MyArray<4> b1;
a1 = a2; // OK The arrays are the same size.
a1 = b1; // FAIL because the size of the array is part of the
// template and thus the type, a1 and b1 are different types.
// Thus this is a COMPILE time failure.
}
【讨论】:
模板类就像一个宏,只是少了很多邪恶。
将模板视为宏。当您使用模板定义类(或函数)时,模板的参数将被替换为类(或函数)定义。
不同之处在于参数具有“类型”,并且在编译期间会检查传递的值,就像函数的参数一样。有效的类型是您的常规 C++ 类型,例如 int 和 char。当您实例化一个模板类时,您传递一个您指定类型的值,并且在模板类定义的新副本中,该值被替换为原始定义中的参数名称。就像一个宏。
您还可以使用“class”或“typename”类型的参数(它们实际上是相同的)。使用其中一种类型的参数,您可以传递类型名称而不是值。就像以前一样,参数名称在模板类定义中的任何地方,只要您创建一个新实例,就会变成您传递的任何类型。这是模板类最常见的用途;每个对 C++ 模板有所了解的人都知道如何做到这一点。
考虑这个模板类示例代码:
#include <cstdio>
template <int I>
class foo
{
void print()
{
printf("%i", I);
}
};
int main()
{
foo<26> f;
f.print();
return 0;
}
它在功能上与这个宏使用代码相同:
#include <cstdio>
#define MAKE_A_FOO(I) class foo_##I \
{ \
void print() \
{ \
printf("%i", I); \
} \
};
MAKE_A_FOO(26)
int main()
{
foo_26 f;
f.print();
return 0;
}
当然,模板版本更安全、更灵活十亿倍。
【讨论】: