赞
踩
typename有两种用法,第一种用于声明模板时,表示模板类型参数,如下所示。在用于模板声明时,typename 和 class 等价,具有同等含义。
template<typename T> class MyClass;
// 等价于
template<class T> class MyClass;
typename的第二种用法,用于表示一个类内所指的类型为一个 “class” 类型(内置类型、自定类型),而非其他(变量、函数类型)。更一般的说法是,typename 用来告诉编译器类中的嵌套名称表示的是一个类型。看下面一个例子。
class MyCls { public: using value = int; }; template<typename T> void func(T t) { typename T::value v = 10; std::cout << v << std::endl; } void test_MyCls() { MyCls m; func(m); } int main() { test_MyCls(); }
我的编译环境为: Ubuntu 20.04 + g++ (GCC) 11.3.0。编译上述代码,能正确编译通过。
若将 func 中的 typename 关键字去掉,则会编译失败,编译结果如下所示:
编译报错的主要原因是,在模板实例化的过程中,T::value 没有被解析为一个类型,如编译结果中红色圈出的部分所示。但可能会存在这样一种疑问,在上面的示例中,模板参数T在模板实例化的过程中解析为 MyCls,而 MyCls::value 在 MyCls 中被声明为 int 的别名,因此 T::value 被实例化为 MyCls::value 后表示的应该就是一个类型。这样直觉的理解看似没问题,但违反了C++中的模板解析规则。在模板实例化之前,会先对语法进行解析,当解析到 T::value 时,T还没被实例化为一个具体的类型,因此 T::value 可能表示的是一个类型,也可能表示的是一个 static 变量,也可能是一个全局的变量,因此 T::value 就存在了歧义,因此编译器编译不通过。为了解决这种起义,C++中规定,当使用类中的嵌套类型时,需要使用 typename 进行声明。
在 《Effective C++》的条款42中,对类中的嵌套类型的描述摘录如下。
template 内出现的名称如果相依于某个template参数,称之为从属名称(dependent names)。如果从属名称在 class 内呈嵌套状,我们称它为从属嵌套名称(nested dependent name)。
因此有了规则 “typename 必须作为嵌套从属类型名称的前缀词”。
“typename 必须作为嵌套从属类型名称的前缀词” 这一规则的例外是,typename 不可以出现在 base classes list 内的嵌套从属名称之前,也不可以在 member initialization list 中作为 base class 修饰符。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。