为什么即使模板类型被声明为“类”,“类型名”仍然是必需的

Why is `typename` still necessary even the template type is declared as `class`?

本文关键字:类型 类型名 仍然是 声明 为什么      更新时间:2023-10-16
#include <type_traits>
using namespace std;
template<class T> struct IsCharType          { enum { value = false }; };    
template<>        struct IsCharType<char>    { enum { value = true }; };    
template<>        struct IsCharType<wchar_t> { enum { value = true }; };
template<class CharType, class = enable_if<IsCharType<CharType>::value>::type>
struct MyString
{
    MyString()
    {}
    MyString(CharType*)
    {}
};
template<class CharType>
MyString<CharType> GetMyString(CharType* str) // error C2923
{
    return MyString<CharType>(str);
}
int main()
{}

我的编译器是VC++ 2013 RC。由于错误 C2923,无法编译上面的代码。

错误 C2923:"我的字符串": 'std::enable_if::value,void>::type' 不是有效的 参数 '' 的模板类型参数

但是,如果我改变

template<class CharType, class = enable_if<IsCharType<CharType>::value>::type>

template<class CharType, class = typename enable_if<IsCharType<CharType>::value>::type>

那就没事了。

我只是想知道为什么这里需要typename?在这种情况下,我有两个原因:

  1. 编译器可以推断第二个模板参数是否为类型名称;因为我把它贴花为class.

  2. 即使编译器无法推断它是否为类型名称,根据SFINAE规则,编译器也不应失败,因为未调用模板函数GetMyString

您尚未将其声明为类。模板参数声明

class = enable_if<IsCharType<CharType>::value>::type

是未命名类型参数的声明。我们可以给这个参数一个显式的名称,例如

class U = enable_if<IsCharType<CharType>::value>::type

因此,在这种情况下,U声明为类型参数("asclass")。

同时,=右边的一切都没有"宣布为阶级"。编译器根据一般规则解释它,而不考虑它恰好是类型参数的默认参数这一事实。

像"由于这是类型参数的默认参数,编译器必须自己意识到这是一个类型"这样的逻辑在这里不适用,因为语言规范没有说它适用。此外,语言标准实际上说

模板声明或定义中使用的名称,即 依赖于模板参数假定不命名类型,除非 适用的名称查找查找类型名称或名称是限定的 通过关键字类型名称。

这意味着编译器需要假设

enable_if<IsCharType<CharType>::value>::type

是一个类型,这与你期望它做的事情相反。

该语言确实有许多上下文,其中无条件地假定依赖名称来命名类型。这包括构造函数初始值设定项列表、基类说明符和详细的类型说明符。但模板类型参数的默认参数不是此类上下文之一。

SFINAE并没有在这里挽救这种情况。顾名思义,SFINAE 在替换时起作用。你的代码永远不会被替换,它会更早地中断。它只是一个无效的模板定义。