#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&gt; :: type'无效 参数''
的模板类型参数
但是,如果我改变
template<class CharType, class = enable_if<IsCharType<CharType>::value>::type>
到
template<class CharType, class = typename enable_if<IsCharType<CharType>::value>::type>
然后就可以了。
我只是想知道为什么typename
在这里是必要的?在这种情况下,我有两个原因:
编译器可以推断第二个模板参数是否是类型名称;因为我将其标记为class
。
即使编译器无法推断出它是否是类型名称,根据SFINAE
规则,编译器也不会失败,因为未调用模板函数GetMyString
。
答案 0 :(得分:1)
您尚未将其声明为类。您的模板参数声明
class = enable_if<IsCharType<CharType>::value>::type
是未命名类型参数的声明。我们可以给这个参数一个明确的名称,例如
class U = enable_if<IsCharType<CharType>::value>::type
所以在这种情况下,U
被声明为类型参数(“as class”)。
同时,=
右侧的所有内容都不是“声明为类”。它由编译器根据一般规则解释,而不考虑它碰巧是类型参数的默认参数。
逻辑如“因为这是类型参数的默认参数,编译器必须自己意识到这是一种类型”这里不适用仅仅因为语言规范没有说它适用。而且,语言标准实际上是说
模板声明或定义中使用的名称,即 取决于模板参数,假定不命名类型,除非 适用的名称查找查找类型名称或名称是合格的 通过关键字typename。
这意味着编译器需要假设
enable_if<IsCharType<CharType>::value>::type
不是类型,这与您的预期相反。
该语言确实有许多上下文,其中无条件地假定依赖名称来命名类型。这包括构造函数初始化列表,基类说明符和详细的类型说明符。但模板类型参数的默认参数不是这种上下文之一。
SFINAE不保存这里的情况。顾名思义,SFINAE在替代时起作用。你的代码永远不会被替换,它会更早地破坏。它只是一个无效的模板定义。