新简化!typename 在 C++20 不再必要
根据提案 P0634-Down with typename,C++20 之后 typename
在有些地方不再必要。
原文主要内容如下:
If X::Y — where T is a template parameter — is to denote a type, it must be preceded by the keyword typename; otherwise, it is assumed to denote a name producing an expression. There are currently two notable exceptions to this rule: base-specifiers and mem-initializer-ids. For example:
template
struct D: T::B { // No typename required here.
};Clearly, no typename is needed for this base-specifier because nothing but a type is possible in that context.
简言之,以前若需要使用模板中的嵌套类型,必须在前面加上 typename
以表示这是一个类型。而编译器供应商很快意识到在许多语境下其实他们知道指定的是否是类型,于是提议将 typename
变为可选项,只在编译器无法确定的语境下 typename
才是必选项。
这项提议内容最终加入了 C++20,因此,C++20 编写模板代码将会进一步简化。举个例子,C++20 可以这样写代码:
struct S {
using X = int;
using R = char;
};
// class template
template<class T,
auto F = typename T::X{}> // typename required
struct Foo {
using X2 = T::X; // typename not required
typedef T::X X3; // typename not required
T::X val; // typename not required
T::R f(T::X x) { // typename not required
typename T::X i; // typename required
return static_cast<T::R>(x); // typename not required
}
auto g() -> T::R { // typename not required
}
template<class U = T::X> // typename not required
void k(U);
};
// function template
template<class T>
T::R // typename not required
f(typename T::X x) // typename required
{
using X2 = T::X; // typename not required
typedef typename T::X X3; // typename required
typename T::X val; // typename required
typename T::R f(); // typename required
auto g() -> T::R; // typename not required
void k(typename T::X); // typename required
auto lam = [](T::X) {}; // typename not required
return static_cast<T::R>(x); // typename not required
}
int main() {
Foo<S> foo;
f<S>(65);
}
示例基本列举了大多数使用 typename
的情境,其中许多情境下 typename
已不再需要。
这里解释一点,为何在类模板与函数模板中,有些情境相同,但是 typename
使用规则却并不相同呢?回答这个问题,再次引用一下 Understanding variadic templates,在这篇文章中我抛出了一个观点:函数模板处理的是数值,类模板处理的是类型。在「类作用域」和「函数作用域」下编译器对于嵌套类型的解析形式是不一样的,前者解析为类型,后者解析为非类型。因此,在函数模板里许多地方依旧需要使用 typename
,而在类模板里则不必要。总之就是,仍旧需要使用 typename
的地方都可能存在歧义,所以需要借助 typename
来标识其为类型。
此外,这里还有一个有趣的点。Modern C++ 中建议使用 using
代替 typedef
来定义类型别名,以前二者除了写法似乎并不区别。但是现在,使用 using
定义嵌套类型的别名时不用指定 typename
,而 typedef
仍然需要。因此,using
代替 typedef
绝对是一种比较好的做法。