Является ли локальный класс зависимым, если он объявлен в шаблоне функции?
Текущие компиляторы C++ (последние gcc, clang) требуют typename
Ключевое слово в примере ниже:
template<class T>
struct A
{
};
template<class T>
void f(T)
{
struct C
{
};
typedef typename A<C>::Type Type; // typename required
}
Если typename
опущен gcc (4.9, 5.0) сообщает об ошибке:
need 'typename' before 'A<f(T)::C>::Type' because 'A<f(T)::C>' is a dependent scope
Этот пример хорошо сформирован в соответствии с моим прочтением стандарта C++11.
Такое поведение, по-видимому, охватывается следующей формулировкой:
[Temp.dep.type]/8
Тип зависит, если он
параметр шаблона,
член неизвестной специализации,
вложенный класс или перечисление, являющееся членом текущего экземпляра,
cv-квалифицированный тип, где cv-неквалифицированный тип является зависимым,
составной тип, построенный из любого зависимого типа,
тип массива, созданный из любого зависимого типа или размер которого определяется константным выражением, которое зависит от значения,
simple-template-id, в котором либо имя шаблона является параметром шаблона, либо любой из аргументов шаблона является зависимым типом или выражением, которое зависит от типа или значения, или
обозначается decltype(выражение), где выражение зависит от типа.
Тем не менее, в соответствии с [class.local] класс C
это локальный класс, а не вложенный класс. Если так, то почему A<C>
лечиться как зависимый?
РЕДАКТИРОВАТЬ
Для бонусных баллов, если пример изменен путем добавления перечисления члена к C
следующее:
template<typename T>
struct A
{
typedef T Type;
};
template<class T>
void f(T)
{
struct C
{
enum { value = T::value };
};
typedef typename A<C>::Type Type; // typename required
}
Должен A<C>
теперь относиться как к зависимым?
3 ответа
Согласно моему пониманию (и нынешней редакции стандарта), C
в вашем примере это не зависит. Ни то, ни другое A<C>::Type
, Итак typename
не требуется.
Существует фундаментальное различие между вложенными классами шаблонов классов и локальными классами в шаблонах функций: последние не могут быть специализированными, поэтому любая ссылка на локальный класс внутри шаблона функции является однородной. То есть в каждой специализации f
, C
относится к классу C
что определено в этом шаблоне функции f
, Это не относится к шаблонам классов, поскольку вы действительно можете явно специализировать элементы самостоятельно (как описано в [temp.expl.spec]
/(1.6)):
template <typename T>
class A { class C{}; };
template <>
class A<int>::C { int i; };
Тем не мение:
Тип зависит, если он
- составной тип, построенный из любого зависимого типа,
Так что, если определение было сделано, как в примере с dyp, C
будет зависеть от того, как он построен из T
,
Существуют неясности в формулировке стандартов, которые обсуждаются в разделе комментариев, например, об определениях функций-членов, которые зависят от T
и как это переносится на зависимость классов.
Следующее - мои рассуждения, надеюсь, это поможет. Местный C
не создается до f
экземпляр. Так, A<C>
не является экземпляром и непрозрачен для компилятора, когда он его видит. Из-за непрозрачности компилятор не может определить, A<C>::Type
является именем вложенного типа или членом данных или методом. Однако по умолчанию компилятор не видит A<C>::Type
как имя вложенного типа. Следовательно, необходима явная спецификация.
Кажется, в стандарте нет ничего, чтобы утверждать, что typename
ключевое слово должно быть необходимо здесь. Формулировка также явно не говорит об ином, что, возможно, привело к тому, что GCC предпринял небольшой шаг в лечении f<T>(T)::C
(будучи локальным классом в специализации шаблона функции) в зависимости от T
- по расширению, это сделало бы A<[f<T>(T)::]C>::Type
зависимый.
Основной дефект 1484 не был затронут специально для этой проблемы, но я думаю, что дополнительный ненормативный текст, который он предлагает, проясняет намерение, и, если бы это было в стандарте, GCC не требовал бы typename
Ключевое слово здесь.