#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
здесь нужно? В этом случае у меня есть две причины:
Компилятор может определить, является ли второй параметр шаблона именем типа; Потому что я удалил это как class
,
Даже если компилятор не может определить, является ли это именем типа, согласно SFINAE
Правило, компилятор не должен потерпеть неудачу, потому что функция шаблона GetMyString
не называется.
Вы не объявили это как класс. Объявление параметров вашего шаблона
class = enable_if<IsCharType<CharType>::value>::type
является объявлением параметра без имени. Мы можем дать этому параметру явное имя, например,
class U = enable_if<IsCharType<CharType>::value>::type
Так что в этом случае это U
который объявлен как параметр типа («как класс»).
Между тем все справа от =
не «объявлен как класс». Он интерпретируется компилятором в соответствии с общими правилами, без учета того факта, что он является аргументом по умолчанию для параметра типа.
Логика, как «, так как это аргумент по умолчанию для тип параметр, компилятор должен понимать, что этот тип «здесь не применяется просто потому, что спецификация языка не говорит, что он применим. Более того, стандарт языка на самом деле говорит
Имя, используемое в объявлении или определении шаблона, и это
Предполагается, что в зависимости от шаблона-параметра тип не будет называться
применимый поиск имени находит имя типа или имя уточняется
по ключевому слову typename.
Это означает, что компилятор требуется предположить тот
enable_if<IsCharType<CharType>::value>::type
является не тип, который противоречит тому, что вы ожидаете от него.
Язык имеет ряд контекстов, в которых зависимое имя безоговорочно считается именем типа. Это включает списки инициализатора конструктора, спецификаторы базового класса и разработанные спецификаторы типов. Но аргументы по умолчанию для параметра типа шаблона не являются одним из таких контекстов.
СФИНА не спасает ситуацию здесь. SFINAE, как следует из названия, работает во время замены. Ваш код никогда не попадает на замену, он ломается намного раньше. Это просто неверное определение шаблона.
Других решений пока нет …