typename的两种用法

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 关键字去掉,则会编译失败,编译结果如下所示:
typename的两种用法_第1张图片

编译报错的主要原因是,在模板实例化的过程中,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 修饰符。

typename的两种用法_第2张图片

你可能感兴趣的:(c++)