0
#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:“MyString”:“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

4

1 回答 1

1

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

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

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

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

所以在这种情况下,它U被声明为类型参数(“作为类”)。

同时,右侧的所有内容=都没有“声明为类”。它由编译器根据一般规则进行解释,而不考虑它恰好是类型参数的默认参数这一事实。

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

在模板声明或定义中使用并且依赖于模板参数的名称被假定为不命名类型,除非适用的名称查找找到类型名称或该名称由关键字 typename 限定。

这意味着编译器需要假设

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

不是类型,与您期望它做的相反。

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

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

于 2013-10-15T02:34:36.097 回答