Почему имя введенного класса иногда не рассматривается как имя шаблона в шаблоне класса?

Источник

В следующих случаях имя внедренного класса рассматривается как имя шаблона самого шаблона класса:

  • за ним следует ‹
  • он используется в качестве аргумента шаблона, который соответствует параметру шаблона шаблона
  • это последний идентификатор в разработанном спецификаторе класса объявления шаблона дружественного класса.

Поэтому я попытался рассмотреть все 3 случая (дополнительно в контексте неоднозначности базы, хотя я думаю, что здесь это не должно иметь значения).

Первый случай кажется простым.

Вопрос - почему не работают закомментированные примеры? Их нет ни в GCC, ни в Clang, поэтому я не думаю, что это проблема реализации.

template <template <class> class> struct A;

template <class T> struct Base {};

template <class T> struct Derived: Base<int>, Base<char>
{
    // #1
    typename Derived::Base<double> d;
    
    // #2
    
    // using a = A<Base>;

    using a = A<Derived::template Base>;

    // #3

    template<class U1>
    friend struct Base;

    // template<class U>
    // friend struct Derived::template Base;
};

Являются ли приведенные выше правила только для самого шаблона, а не для баз? Если да, то каковы правила для баз, особенно для последних двух случаев?


person ledonter    schedule 08.05.2018    source источник
comment
Я могу получить аналогичную копию #2, но меньшего размера. Проблема также исчезает, если вы используете ::Base, интересно.   -  person Ryan Haining    schedule 08.05.2018


Ответы (1)


Соответствующее правило здесь — [temp.local]/4:

Поиск, который находит внедренное имя класса ([class.member.lookup]), может привести к неоднозначности в определенных случаях (например, если он найден более чем в одном базовом классе). Если все найденные имена внедренных классов относятся к специализациям одного и того же шаблона класса и если это имя используется как имя шаблона, ссылка относится к самому шаблону класса, а не его специализации, и не является двусмысленным. [ Пример:

template <class T> struct Base { };
template <class T> struct Derived: Base<int>, Base<char> {
  typename Derived::Base b;             // error: ambiguous
  typename Derived::Base<double> d;     // OK
};

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

Что, я думаю, означает, что это ошибка gcc и clang. В:

using a = A<Base>;

Все найденные имена внедренных классов действительно относятся к специализациям одного и того же шаблона класса (Base<T>) и это имя используется как имя шаблона (поскольку оно аргумент шаблона для параметра шаблона шаблона), так что это должно просто ссылаться на сам шаблон класса и не быть двусмысленным.

person Barry    schedule 08.05.2018
comment
Итак, вы говорите, что оба закомментированных примера должны компилироваться? Меня просто беспокоит сообщение об ошибке: «База» не является ни функцией, ни функцией-членом; нельзя объявить другом, ничего не говорит о двусмысленности; Я понимаю, что не надо, но все же... - person ledonter; 08.05.2018
comment
@ledonter Думаю, да. - person Barry; 08.05.2018