Почему `typename` все еще необходимо, даже если тип шаблона объявлен как` class`?

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 :: значение, 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 не вызывается.

Теги:
templates
compiler-errors
type-deduction
typename

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, как следует из названия, работает во время замены. Ваш код никогда не подменяет, он ломается намного раньше. Это просто неверное определение шаблона.

  • 0
    Я имею в виду class = Something явно указывает, что SomeThing - это имя типа. Компилятор должен знать это.
  • 0
    @xmllmx: Как я уже говорил выше, в языке C ++ не существует понятия «должен знать». Есть языковая спецификация, которая говорит, что такое правильно сформированный код и что такое неправильно сформированный код. И если какой-то код неправильно сформирован, компилятор должен сообщить об этом (и, как правило, не может скомпилировать его), даже если вам может показаться, что компилятор «должен знать», что делать. В спецификации языка не сказано, что в этом случае компилятор должен это выяснить. Вместо этого он говорит, что в этом случае требуется typename .
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню