Yes, it is a non-type parameter. You can have several kinds of template parameters
是的,它是一个非类型参数。您可以使用多种模板参数
- Type Parameters.
- Types
- Templates (only classes and alias templates, no functions or variable templates)
- Non-type Parameters
- Pointers
- References
- Integral constant expressions
What you have there is of the last kind. It's a compile time constant (so-called constant expression) and is of type integer or enumeration. After looking it up in the standard, i had to move class templates up into the types section - even though templates are not types. But they are called type-parameters for the purpose of describing those kinds nonetheless. You can have pointers (and also member pointers) and references to objects/functions that have external linkage (those that can be linked to from other object files and whose address is unique in the entire program). Examples:
你在那里拥有的是最后一种。它是一个编译时间常量(所谓的常量表达式),是整型或枚举型的。在标准中查找之后,我不得不将类模板上移到Types部分--尽管模板不是类型。但出于描述这些类型的目的,它们被称为类型参数。您可以拥有指向具有外部链接的对象/函数的指针(以及成员指针)和引用(可以从其他对象文件链接到这些对象/函数,并且其地址在整个程序中是唯一的)。例如:
Template type parameter:
模板类型参数:
template<typename T>
struct Container {
T t;
};
// pass type "long" as argument.
Container<long> test;
Template integer parameter:
模板整型参数:
template<unsigned int S>
struct Vector {
unsigned char bytes[S];
};
// pass 3 as argument.
Vector<3> test;
Template pointer parameter (passing a pointer to a function)
模板指针参数(将指针传递给函数)
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 reference parameter (passing an integer)
模板引用参数(传递整数)
template<int &A>
struct SillyExample {
static void do_it() { A = 10; }
};
// pass flag as argument
int flag;
SillyExample<flag> test;
Template template parameter.
模板模板参数。
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;
A template without any parameters is not possible. But a template without any explicit argument is possible - it has default arguments:
没有任何参数的模板是不可能的。但是没有任何显式参数的模板是可能的-它有默认参数:
template<unsigned int SIZE = 3>
struct Vector {
unsigned char buffer[SIZE];
};
Vector<> test;
Syntactically, template<>
is reserved to mark an explicit template specialization, instead of a template without parameters:
在语法上,模板<>保留用于标记显式模板专门化,而不是不带参数的模板:
template<>
struct Vector<3> {
// alternative definition for SIZE == 3
};
It's perfectly possible to template a class on an integer rather than a type. We can assign the templated value to a variable, or otherwise manipulate it in a way we might with any other integer literal:
完全有可能将类模板化为整数而不是类型。我们可以将模板化的值赋给一个变量,或者以处理任何其他整型文字的方式对其进行操作:
unsigned int x = N;
In fact, we can create algorithms which evaluate at compile time (from 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
}
You templatize your class based on an 'unsigned int'.
你根据一个‘unsign int’来模板化你的类。
Example:
示例:
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.
}
A template class is like a macro, only a whole lot less evil.
模板类就像一个宏,只是少了很多坏处。
Think of a template as a macro. The parameters to the template get substituted into a class (or function) definition, when you define a class (or function) using a template.
把模板想象成一个宏。当您使用模板定义类(或函数)时,模板的参数将被替换到类(或函数)定义中。
The difference is that the parameters have "types" and values passed are checked during compilation, like parameters to functions. The types valid are your regular C++ types, like int and char. When you instantiate a template class, you pass a value of the type you specified, and in a new copy of the template class definition this value gets substituted in wherever the parameter name was in the original definition. Just like a macro.
不同之处在于参数有“类型”,传递的值在编译期间被检查,就像函数的参数一样。有效的类型是常规的C++类型,如int和char。实例化模板类时,传递指定类型的值,并且在模板类定义的新副本中,在原始定义中参数名称所在的位置替换此值。就像宏一样。
You can also use the "class
" or "typename
" types for parameters (they're really the same). With a parameter of one of these types, you may pass a type name instead of a value. Just like before, everywhere the parameter name was in the template class definition, as soon as you create a new instance, becomes whatever type you pass. This is the most common use for a template class; Everybody that knows anything about C++ templates knows how to do this.
你也可以使用“class”或“typeObject”类型作为参数(它们实际上是一样的)。对于这些类型之一的参数,可以传递类型名称而不是值。就像以前一样,只要你创建了一个新的实例,模板类定义中的参数名就会变成你传递的任何类型。这是模板类最常见的用法;每个了解C++模板的人都知道如何做到这一点。
Consider this template class example code:
请考虑以下模板类示例代码:
#include <cstdio>
template <int I>
class foo
{
void print()
{
printf("%i", I);
}
};
int main()
{
foo<26> f;
f.print();
return 0;
}
It's functionally the same as this macro-using code:
它在功能上与此宏相同-使用代码:
#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;
}
Of course, the template version is a billion times safer and more flexible.
当然,模板版本的安全性和灵活性要高出10亿倍。
Yes, C++ template can include a numeric value as the template parameter, as simply as mytype<1>
and mytype<2>
.
是的,C++模板可以包括一个数值作为模板参数,就像mytype<1>和mytype<2>一样简单。
As class template, std::array<class, size_t>
from the C++ header <array>
is the perfect example of using a value as the template, along with a typename.
( Read more about std::array
: https://cplusplus.com/reference/array/array ,
or https://en.cppreference.com/w/cpp/container/array )
In C++, which is a strict type-specific language, the use of such template with different parameters is automatically implemented as each different type. std::array<int, 5>
and std::array<int, 6>
and std::array<long long, 5>
and std::array<long long, 6>
are all explicitly different types, fixed at compile-time. They are interpreted as different as mytypeA, mytypeB, mytypeC, and mytypeD. Those classes prepare their member variables of different types and sizes. Thus they are not compatible, and the compiler will ring an alarm with an error if you place these class instances at each other's spot, unless 'conversion operator function' is implemented inside. Using this feature, you can purposely prohibit the type conversion between similar classes, by assigning them different values at template parameter.
As function template, we can handle the compile-time fixed array of every possible size, for example.
Not limited to those above, there are so many other possibilities.
For example, as you wonder the utility and worthiness of template with only numeric values, we can implement matrix(math term of number array) as a type by combining class template and function template. We don't need the runtime pointer calculation or std::vector container!
This is just a sketch of conceptual code.
template <size_t ROW, size_t COL>
class matrix {
int64_t data[ROW][COL];
matrix() { /* constructor */ }
matrix(int64_t** pointer_of_numeric_array) { /* constructor */ }
matrix(int64_t[ROW][COL]& assigning_data) { /* constructor */ }
};
template <size_t leftR, size_t leftC, size_t rightR, size_t rightC>
matrix<leftR, rightC> operator* // look at the return type!
(const matrix<leftR, leftC>& mat1, const matrix<rightR, rightC>& mat2) {
// so that we can control the relation of type parameters!
matrix<leftR, rightC> result;
/* do the math here */
return result;
}
// later:
matrix<3, 5> mydata1;
matrix<5, 7> mydata2;
auto result = mydata1 * mydata2;
the type of result
will be matrix<3, 7>
, a new and different type.
After all, template made them possible by writing only one efficient code!
毕竟,模板使它们成为可能,因为它只编写了一个高效的代码!
更多回答
Johannes, are templates filed under "types"? I thought they were what types can be made from, but not types themselves?
约翰尼斯,模板是放在“类型”下面的吗?我以为它们是可以制造的类型,但不是类型本身?
@sbi see the explanation: "After looking it up in the standard, i had to move class templates up into the types section - even though templates are not types. But they are called type-parameters for the purpose of describing those kinds nonetheless.". Footnote 126 on 14.1/2 says so. It's just a classification made to make non-type parameters something that declares a value/reference and type-parameters be something declaring a type name or template name.
@SBI参见解释:“在标准中查找之后,我不得不将类模板上移到类型部分--尽管模板不是类型。但是为了描述那些类型,它们被称为类型参数。”14.1/2号文件的脚注126是这样说的。这只是一种分类,目的是使非类型参数声明值/引用,而类型参数声明类型名称或模板名称。
@JohannesSchaub-litb so there is no way to type template with let say std::string? like template<std::string S> class with some static counter in it to create unique id for every different string? hashing string to int would be the only way unfortunately right?
@约翰内斯堡Schaub-litb所以没有办法用STD::STRING输入模板?像模板类中有一些静态计数器,以便为每个不同的字符串创建唯一的id?不幸的是,将字符串散列到int将是唯一的方法,对吗?
I'd love to see this answer completed with template class member objects, i.e. template<typename C, typename R, typename P1, typename P2> struct mystruct<R(C::*)(P1,P2)>
我希望看到模板类成员对象完成这个答案,即模板struct mystruct
The piece of code with SillyExample
can't be compiled by GCC 4.8.4. The first error is the value of ‘flag’ is not usable in a constant expression
. There are other errors as well
这段带有SillyExample的代码不能被GCC 4.8.4编译。第一个错误是'flag'的值在常量表达式中不可用。还有其他的错误
You can also use type static constexpr int
instead of your enum
. So the Factorial<0>
template would have static constexpr int value = 1
, and template <int N> struct Factorial
can have static constexpr int value = N * Factorial<N - 1>::value;
您还可以使用类型静态的conexpr int而不是枚举。因此,<0>模板将具有静态常量expr int值=1,而模板结构阶乘可以具有静态常量expr int值=N*阶乘::Value;
@bobobobo this was answered before C++11 and constexpr
.
@bobobobo在C++11和conexpr之前回答了这个问题。
I got error when using enum as parameter. <Non-type template argument is not a constant expression>. The possible value of enum is finite, is there any way to make it work.
使用枚举作为参数时出错。<非类型模板参数不是常量表达式>。枚举的可能值是有限的,有什么方法可以让它工作吗?
@JustinMeiners same reasoning doesn't get applied if you post a "duplicate question" expecting to get an answer not from 30 years ago... And that comment is useful to me.
@JustinMeiners同样的推理不适用于如果你发布了一个“重复问题”,希望得到一个不是30年前的答案……这句话对我很有用。
I am very late to the discussion, but what this the advantage of using this method compared to initialization via a constructor? Is it because the initialization via a constructor (foo f = foo(26)
) happens in runtime while the template method happens in compile time?
我讨论得很晚,但是与通过构造函数进行初始化相比,使用这种方法有什么好处呢?是因为通过构造函数(foo f=foo(26))进行的初始化发生在运行时,而模板方法发生在编译时吗?
我是一名优秀的程序员,十分优秀!