Проект N3337 стандарта C ++ 11 гласит [namespace.udecl]
Объявление-использование вводит имя в декларативную область, в которой появляется объявление-использование.
Каждое объявление об использовании является объявлением и объявлением члена и поэтому может использоваться в определении класса.
В объявлении использования, используемом в качестве объявления члена, спецификатор nested-name должен называть базовый класс
класс определяется.
Это обычно используется для того, чтобы сделать защищенную typedef внутри общедоступного базового класса в производном классе, как в следующем примере, который успешно компилируется в последней версии Clang:
struct A
{
protected:
typedef int Type;
};
struct B : A
{
using A::Type;
};
B::Type x;
Объявление-использование может ссылаться на класс шаблона. Это компилирует:
struct A
{
protected:
template<typename T>
struct Type
{
};
};
struct B : A
{
using A::Type;
};
B::Type<int> x;
Также возможно ссылаться на шаблон в зависимом базовом классе. Следующие компиляции успешно (с комментарием typedef.)
template<typename T>
struct A
{
protected:
template<typename U>
struct Type
{
};
};template<typename T>
struct B : A<T>
{
using /* typename */ A<T>::Type; // A<T> is dependent, typename required?
// typedef Type<int> IntType; // error: unknown type name 'Type'
};
B<int>::Type<int> x;
Раскомментировать typename
вызывает ошибку при создании экземпляра B<int>
: «ошибка: ключевое слово ‘typename’, используемое не для типа».
Раскомментирование typedef вызывает ошибку при разборе B
до его первого экземпляра. Я предполагаю, что это потому, что компилятор не лечит Type
в качестве зависимого имени типа.
Последний абзац [namespace.udecl]
предполагает, что использование-объявления могут указывать зависимые имена, и что typename
ключевое слово должно использоваться для устранения неоднозначности дальнейшего использования введенного имени:
Если объявление использования использует ключевое слово typename и указывает зависимое имя (14.6.2), вводится имя
с помощью объявления-использования обрабатывается как typedef-имя
Мое чтение [temp.dep]
предполагает, что A<T>::Type
это зависимое имя. Логично следует, что имя, введенное объявлением использования, также должно быть зависимым, но [temp.dep]
явно не упоминается случай зависимого объявления об использовании. Я что-то пропустил?
Проблема в том, что Type
это не класс, а шаблон класса. Вы можете сделать следующее (таким образом вы сообщаете компилятору, что Type
шаблон класса в области видимости B
):
template<typename T>
struct B : A<T>
{
using A<T>::Type;
typedef typename B::template Type<int> IntType;
};
На самом деле, во втором примере, чтобы написать typedef
за IntType
Вы должны сделать то же самое.
Да, объявление использования члена класса с зависимым квалифицированным идентификатором вводит зависимое имя.
[Namespace.udecl]Объявление-использование вводит имя в декларативную область, в которой появляется объявление-использование.
Если введенное имя является зависимым, оно остается зависимым — я не могу найти ничего, что могло бы предложить иначе.
Однако синтаксис объявления использования не позволяет указать, что зависимое имя является шаблоном. Последующие ссылки на зависимое имя Type
в B
может или не может ссылаться на шаблон, поэтому Type<int>
не может быть проанализирован
В следующем примере демонстрируется правильное использование зависимого объявления использования.
template<typename T>
struct A
{
protected:
struct Type
{
typedef int M;
};
};template<typename T>
struct B : A<T>
{
using typename A<T>::Type;
typedef typename Type::M Type2;
};
B<int>::Type2 x;