什么时候需要"typename"关键字?
When is the "typename" keyword necessary?
可能重复:
正式来说,typename是什么意思
我必须把模板和typename关键字放在哪里以及为什么?
考虑以下代码:
template<class K>
class C {
struct P {};
vector<P> vec;
void f();
};
template<class K> void C<K>::f() {
typename vector<P>::iterator p = vec.begin();
}
为什么在这个例子中需要"typename"关键字?是否存在必须指定"typename"的其他情况?
简短回答:每当引用嵌套名称时,该名称是依赖名称,即嵌套在具有未知参数的模板实例中。
长话短说:C++中有三层实体:值、类型和模板。所有这些都可以有名称,仅凭名称并不能告诉你它是哪一层实体。相反,关于名称实体性质的信息必须从上下文中推断出来。
每当这种推断不可能时,你必须指定它:
template <typename> struct Magic; // defined somewhere else
template <typename T> struct A
{
static const int value = Magic<T>::gnarl; // assumed "value"
typedef typename Magic<T>::brugh my_type; // decreed "type"
// ^^^^^^^^
void foo() {
Magic<T>::template kwpq<T>(1, 'a', .5); // decreed "template"
// ^^^^^^^^
}
};
这里必须解释名称Magic<T>::gnarl
、Magic<T>::brugh
和Magic<T>::kwpq
,因为无法判断:由于Magic
是一个模板,类型Magic<T>
的性质取决于T
——例如,可能存在与主模板完全不同的专业化。
Magic<T>::gnarl
之所以成为一个依赖名称,是因为我们在一个模板定义中,其中T
是未知的。如果我们使用Magic<int>
,情况会有所不同,因为编译器知道(你保证!(Magic<int>
的完整定义。
(如果你想自己测试,这里有一个Magic
的示例定义,你可以使用。为了简洁起见,请原谅在专门化中使用constexpr
;如果你有一个旧的编译器,可以随意将静态成员常量声明更改为旧风格的C++11之前的形式。(
template <typename T> struct Magic
{
static const T gnarl;
typedef T & brugh;
template <typename S> static void kwpq(int, char, double) { T x; }
};
template <> struct Magic<signed char>
{
// note that `gnarl` is absent
static constexpr long double brugh = 0.25; // `brugh` is now a value
template <typename S> static int kwpq(int a, int b) { return a + b; }
};
用法:
int main()
{
A<int> a;
a.foo();
return Magic<signed char>::kwpq<float>(2, 3); // no disambiguation here!
}
需要typename
关键字,因为iterator
是P
的依赖类型。编译器无法猜测iterator
是指一个值还是一个类型,所以除非您大喊typename
,否则它会假设它是一个值。只要存在依赖于模板参数的类型,在类型或值都有效的上下文中,就需要它。例如,由于基类必须是类型,因此不需要typename
作为基类。
关于同一主题,有一个template
关键字用于让编译器知道某个依赖名称是模板函数而不是值。
只要类型名称依赖于模板参数,就需要typename关键字(这样编译器就可以"知道"标识符的语义(类型或值(,而无需在第一次遍历时使用完整的符号表(。
含义不同,也不太常见,单独的typename关键字在使用通用模板参数时也很有用:http://ideone.com/amImX
#include <string>
#include <list>
#include <vector>
template <template <typename, typename> class Container,
template <typename> class Alloc = std::allocator>
struct ContainerTests
{
typedef Container<int, Alloc<int> > IntContainer;
typedef Container<std::string, Alloc<int> > StringContainer;
//
void DoTests()
{
IntContainer ints;
StringContainer strings;
// ... etc
}
};
int main()
{
ContainerTests<std::vector> t1;
ContainerTests<std::list> t2;
t1.DoTests();
t2.DoTests();
}
- Visual Studio 2015:Extern "C" 和 "export" 关键字
- C++中的"inline"关键字
- 如何确保C++函数在定义之前声明(如override关键字)
- 谷歌模拟和覆盖关键字
- 使用 'typename' 关键字将非类型视为依赖上下文中的类型
- SFINAE的Typename关键字
- 有什么技巧可以避免在模板类中使用"typename"关键字吗
- 为什么在VS2015中模板相关的嵌套类型名称中不需要typename关键字?
- 标准::函数的 typename 关键字
- 为什么我应该使用"typename"关键字来定义?
- 为什么C++编译器在这种特殊情况下需要 "typename" 关键字?
- 模板参数列表中的额外 typename 关键字:是否有效
- 为什么在函数参数列表中重复typename关键字是必要的
- typename关键字是如何工作的
- 为什么关键字"typename"在限定的从属名称之前需要,而不是在限定的独立名称之前?
- 为什么C++17在模板中有两个关键字(class和typename)用于相同的目的
- 什么时候需要"typename"关键字?
- 即使typename关键字也会抛出Typedef错误
- 再次强调typename和template关键字
- 多个嵌套的依赖名称 - 将 typename 关键字粘贴在哪里