#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 :: значение, 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 не спасает ситуацию здесь. SFINAE, как следует из названия, работает во время замены. Ваш код никогда не подменяет, он ломается намного раньше. Это просто неверное определение шаблона.
class = Something
явно указывает, чтоSomeThing
- это имя типа. Компилятор должен знать это.typename
.