模板语句的“类”部分有什么作用?
示例:
template <class T>
class Something
{
public:
Something(const T &something);
}
还有什么可以去那里?我通常只看“课”。
答案 0 :(得分:8)
class
关键字与大多数typename
关键字的含义相同。它们都表明T是一种类型。
关键字class
和typename
之间的唯一区别是class
可用于向模板提供类模板模板参数,而typename
则不能。考虑:
template<template <class T> class U> // must be "class"
std::string to_string(const U<char>& u)
{
return std::string(u.begin(),u.end());
}
您可以替代class
或typename
关键字的唯一其他事项是整数类型。例如:
template<std::size_t max>
class Foo{...};
...
Foo<10> f;
有关具体示例,请查看标准库中的std::bitset<N>
。
答案 1 :(得分:8)
要定义模板参数,您需要告诉编译器参数是类型还是值。
如果我没记错的话,C ++委员会不愿意在C ++语言中添加新关键字,因此,他们决定授权以下符号:
template<int I>
int getTwice()
{
return I * 2 ;
}
template<class T>
std::string getType(const T & t)
{
return typeid(t).name() ;
}
void doSomething()
{
std::cout << "25 : " << getTwice<25>() << std::endl ;
std::cout << "5 : " << getTwice<5>() << std::endl ;
std::cout << "type(25) : " << getType(25) << std::endl ;
std::cout << "type(25.5) : " << getType(25.5) << std::endl ;
std::cout << "type(abc) : " << getType("abc") << std::endl ;
}
以g ++表示哪些输出:
25 : 50
5 : 10
type(25) : i
type(25.5) : d
type(abc) : A4_c
第一个表示法是一个值的模板。所以,我们在模板声明中有值的类型:
// "I" is the value, and "int" is the type of the value
template <int I>
第二种表示法是未知类型的模板,而且类型不是“已知”的事实由“class”关键字标记。因此,在这种情况下,“类”意味着“类型”。
// "T" is a type... And "class" is the "this-is-a-type" keyword
template <class T>
你会注意到,使用第二种表示法,尽管有class关键字,但T可能是...一个int或另一种内置类型。但是,比起添加新关键字更好的是有这种好奇心,你不同意吗?...
一切都很好,直到有人写下面的代码:
template<class T> // T could be any STL container, for example a vector<char>
void printContainerData(const T & t)
{
std::cout << "aVector:" ;
for(T::const_iterator it = t.begin(), itEnd = t.end(); it != itEnd; ++it)
{
std::cout << " " << (*it) ;
}
std::cout << std::endl ;
}
其中T :: const_iterator是一个类型,当然......但是,它可能是类型为T的类的静态成员,因此是一个值。编译器可能很混乱。
解决方案是告诉编译器T :: const_iterator是真的一个类型......这会带有这种表示法:
for(class T::const_iterator it = t.begin(), // etc.
但这被认为不可能/正确(类是关于类声明,不是吗?)。所以,拖着他们的脚,他们确定了一个关键字 确实需要告诉编译器符号是一个类型,而不是一个值。
我认为,“type”没有被考虑,因为将其作为关键字会破坏很多代码。所以改为使用typename。使用typename,我们可以写:
for(typename T::const_iterator it = t.begin(), // etc.
为了保持一致,我们应该使用:
template <typename T>
当T应该是一个类型而不是一个值时。 但出于兼容性原因,旧的表示法:
template <class T>
仍然有权。
eben提出了上面的答案,我想回答一个答案,因为它非常有趣:
template<template <class T> class U> // must be "class"
std::string to_string(const U<char>& u)
{
return std::string(u.begin(),u.end());
}
我只会评论它的“含义”(这个代码不能与我的g ++编译器上的STL容器一起使用,但这不是重点,我猜):有一刻,它对U的约束说:“ U是一个模仿T类的类。这是部分:
template <class T> class U
也可写:
template <typename T> class U
因为U实际上只是一个类(而不是内置类型),而T是一个类型,任何类型。
下一行,它说U专门用于char:
std::string to_string(const U<char>& u)
因此,如果U被声明为:
,则此“通用代码”仅适用于U.template<typename T>
class U
{
// Etc.
} ;
U在一个char上实例化了:
U<char> u ;
// etc.
to_string(u)
但有一件事被遗忘了:Eben提出的符号可以用两种方式写出来:
template<template <class T> class U>
std::string to_string(const U<char>& u)
template<template <typename T> class U>
std::string to_string(const U<char>& u)
第二个“class”关键字本身不是“type”关键字。它是一种类型,是T上的模板类。因此令人困惑的符号。
另一种写Eben代码的方法,删除上面的约束,就像:
template<typename U>
std::string to_string(const U & u)
{
return std::string(u.begin(),u.end());
}
让编译器发挥其魔力:
std::list<char> myList ;
// etc.
std::cout << to_string(myList) << std:endl ;
(例如,Eben的代码不适用于我的g ++编译器上“char”模板化的STL容器...)
答案 2 :(得分:4)
您还可以使用以下作为模板参数:
short, int, long, bool
等
一些例子:
template<typename T, int N=4, bool Flag>
class myClass { /*...*/ };
template<typename T, const int& Reference, myClass * Pointer>
class someClass { /*...*/ };
template<typename T, int (T::*MemberFunctionPtr)(int, int)>
class anotherClass { /*...*/ };