Подтвердить что ты не робот

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

Я ответил на этот вопрос, используя конструкцию С++, с которой я не знаком. Я хочу знать, является ли это законным или разрешено как g++ (6.3.0), так и clang++ (3.5.0) по ошибке. Пример доступен онлайн:

#include <iostream>

template <typename T>
struct Base
{
    using Type = int;
};

template <typename T>
struct intermediate : Base<T>
{
    // 'Type' is not defined here, which is fine
};

template <typename T>
struct Derived : intermediate<T>
{
    using Type = typename Derived<T>::Type; // Is this legal?
//    using Type = typename intermediate<T>::Type; // Normal way of doing it
};

int main()
{
    Derived<void>::Type b = 1;
    std::cout << b << std::endl;
}

Обновление

Как указано в комментариях (underscore_d) <T> не требуется в классе Derived. То есть, это совершенно нормально:

using Type = typename Derived::Type;
4b9b3361

Ответ 1

Мне потребовалось некоторое время, чтобы понять (очень хороший) вопрос, поэтому позвольте мне сначала объяснить, как я понимаю:

На уровне шаблона область шаблона базового класса не проверяется, если этот шаблон базового класса зависит от параметра шаблона (cf, например, этот answer).  Следовательно, тип, объявленный using Type = int в шаблоне класса с параметром шаблона, например template <typename T> struct Base, не будет виден классу, полученному из этого шаблона базового класса, например template <typename T> struct intermediate : Base<T>, если только не используется квалифицированное имя, то есть typename Base<T>::Type).

На уровне класса, напротив, такое объявление типа было бы видно даже при использовании без квалифицированного имени. Поэтому при создании экземпляра вышеупомянутого класса шаблонов имя типа, которое было "невидимым" на уровне шаблона, станет видимым на уровне инцессированных классов.

Итак, если подкласс на уровне шаблона будет определять тип с тем же (неквалифицированным) именем в качестве шаблона базового класса, это не будет очевидно на уровне шаблона.  Но может ли такая ситуация привести к (возможно, несоответствующему) переопределению типа при создании экземпляра шаблона, так как тогда оба определения типов, основанные на именах неквалифицированных типов, могут стать видимыми под тем же именем?

Как указывал nm, который прокомментировал эту ссылку в своем комментарии, определение типа привязывается к представлению в точке определения шаблона, даже если на уровне экземпляра будет разрешен другой тип:

Независимые имена просматриваются и привязываются в точке шаблона определение. Это связывание выполняется, даже если в шаблоне лучший вариант.

Таким образом, это действительно законно, поскольку это не приведет к неправильному переопределению типа.

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

template <typename T>
struct Base
{
    using Type = int;
};

template <typename T>
struct intermediate : Base<T>
{
    // 'Type' is not defined here, which is fine
};

template <typename T>
struct Derived : intermediate<T>
{
    //using Type = typename Derived<T>::Type; // legal.
    using Type = const char*; // Legal, too!
};

int main()
{
    Derived<void>::Type b = "OK, but is this actually intended?";
    std::cout << b << std::endl;
}

Таким образом, подкласс не "наследует", а "закрывает" соответствующий тип. Если вы хотите "наследовать", нужно написать:

struct Derived : intermediate<T>
{
    using typename intermediate<T>::Type; // Inherits the type.
};