Имя введенного класса как тип - программирование
Подтвердить что ты не робот

Имя введенного класса как тип

Учитывая следующий код,

template <class> using void_t = void;
template <class C, class = void> struct X { enum { v = 0 }; };
template <class C> struct X<C, void_t<typename C::T> > { enum { v = 1 }; };
struct T { };
int main() { return X<T>::v; }

каково должно быть возвращение? GCC и MSVC говорят 1, говорит Кланг 0.

4b9b3361

Ответ 1

Я думаю, что Кланг здесь. Правило в [class.qual]:

В поиске, в котором имена функций не игнорируются, а спецификатор вложенных имен назначает класс C:

  • если имя, указанное после вложенного имени-спецификатора, при поиске на C - это имя впрыскиваемого класса C ([class]) или
  • [... неуместно здесь...]

вместо этого вместо имени будетменоваться конструктор класса C [Примечание. Например, конструктор не является приемлемым результатом поиска в специфицированном спецификаторе типа, поэтому конструктор не будет использоваться вместо имени введенного класса. - end note] Такое имя конструктора должно использоваться только в идентификаторе-деклараторе объявления, которое называет конструктор или декларацию использования. [ Пример:

struct A { A(); };
struct B: public A { B(); };

A::A() { }
B::B() { }

B::A ba;            // object of type A
A::A a;             // error, A​::​A is not a type name
struct A::A a2;     // object of type A

- конец примера]

typename C::T - это то же самое, что и A::A, это поиск, в котором имена функций не игнорируются (typename не приводит к игнорированию имен функций). Итак, в typename C::T, когда C есть T, имя T считается именем конструктора. Поскольку это не имя типа, мы должны получить сбой замены и вернуться к основному шаблону.

Подано 86818.

Ответ 2

Чтобы завершить ответ Барри, typename сообщает компилятору, что следующее имя является типом для анализа, выполненного до создания шаблона. После инстанцирования поиск имени выполняется так, как если бы имя типа не было, [temp.res]/4:

Обычный квалифицированный поиск имени используется для поиска идентификатора с идентификатором даже в присутствии typename.

Так что Кланг прав. Чтобы получить согласованное поведение компилятора, вы можете использовать специфицированный спецификатор типа struct C::T вместо typename C::T:

template <class> using void_t = void;
template <class C, class = void> struct X { enum { v = 0 }; };
template <class C> struct X<C, void_t<struct C::T> > { enum { v = 1 }; };
struct T { };
int main() { return X<T>::v; }