Использование ключевого слова typename с typedef и новым
Рассмотрим этот код,
template<class T>
struct Sample
{
typename T::X *x; //declare pointer to T X
};
В приведенном выше коде ключевому слову typename
требуется компилятор, чтобы он мог устранить неоднозначность между вложенными типами и вложенными значениями в шаблонах. Это означает, что в отсутствие ключевого слова typename
компилятор интерпретирует это как умножение T:: X на x,
T::X *x; //multiply T::X with x
Поэтому в таких ситуациях, где может возникнуть неоднозначность, ключевое слово typename
становится необходимость, чтобы устранить неоднозначность. Но есть несколько ситуаций, когда сам контекст устраняет двусмысленности. другая тема обсуждает контексты базового класса и функциональных параметров (последнее не устраняет неоднозначность). В этом разделе я особенно хочу обсудить другие два контекста, которые кажутся недвусмысленными, но нам все еще требуется написать typename
,
typedef typename T::X xtype;
pX = new typename T::X;
В этих двух ситуациях ключевые слова typedef
и new
дают достаточно ясно компилятору, что все, что следует за ним, это тип, не.
Итак, мой вопрос: зачем компиляторам по-прежнему нужно ключевое слово typename
, даже в недвусмысленных ситуациях, например, когда мы используем typedef
и new
?
EDIT (после прочтения этого ответа от Johannes Schaub):
//typedef NOT followed by a type!
int typedef A;
Этот синтаксис просит меня немного изменить свой вопрос, так что точка, которую я пытаюсь сделать, может быть замечена другими.
Рассмотрим это,
T::X typedef *x;
Итак, из контекста все еще достаточно ясно для компилятора, что T:: X является типом, независимо от того, появляется он перед typedef
или после typedef
. Если С++ не позволяет нам писать typedef 5 five
или typedef T::value t_value
(где T:: значение является значением), наличие typedef
само удаляет все неоднозначности, и поэтому typename
представляется ненужным требованием Стандарт (в таких ситуациях). Тот же аргумент справедлив и для new
.
Кроме того, я написал шаблон класса, который использует эту структуру в качестве аргумента шаблона:
struct A
{
struct X { string name; };
static const int X = 100;
};
Я особенно хочу знать, является ли следующий код (из конструктора) правильным (переносимым) или нет,
//two interesting statements
pX = new typename T::X; //T::X means struct X
product = T::X * p; //but here, T::X means int X
Полный код здесь на ideone. Пожалуйста, взгляните на него перед ответом.: -)
Ответы
Ответ 1
Синтаксис С++ более сумасшедший.
// typedef NOT followed by a type!
int typedef A;
// new NOT followed by a type!
new (0) int;
Другие прокомментировали ваш пример. Спецификатор typename
не дает поиска, игнорируя имена не-типа. Поэтому, если вы скажете new typename T::X
, и есть имя объекта X
в T
, оно все равно будет найдено вместо имени типа X
(однако GCC игнорирует имена не-типа при поиске имени после typename
, но это не соответствует стандартам).
Ответы на изменения:
Рассмотрим это,
T::X typedef *x;
Итак, из контекста все еще достаточно ясно для компилятора, что T:: X является типом, независимо от того, появляется ли он до typedef или после typedef.
Компилятор должен знать, когда спецификаторы декларации и (т.е. раздел "тип" и когда начинается секция декларатора (например, раздел "имена" ). Существуют объявления, где раздел типа пуст:
// constructor definitions don't need a type section
MyClass::MyClass() { }
// conversion function definitions don't need a type section
MyClass::operator int() { }
Если имя, которое вы указали, не является типом, раздел типа заканчивается, и начинается раздел имени. Высказывание T::X
сообщает компилятору:
Теперь я хочу определить T::X
.
Он читается слева направо, поэтому он подумает, что вы забыли точку с запятой, когда она встречает typedef
. Внутри классов интерпретация немного отличается, но тоже такая. Это простой и эффективный анализ.
Тот же аргумент верен и для нового.
Я склонен согласиться с вами здесь. Синтаксически это должно быть однозначным, если вы оставьте круглые скобки. Поскольку я никогда не писал парсер С++, могут быть скрытые ловушки, которых я не вижу.
Каждое добавление typename
в угловые случаи языка, как в new
, потенциально потребует значительного объема дизайна как для компиляторов, так и для авторов стандартов, при этом все еще требуя typename
для подавляющего большинства других случаев, когда это необходимо, Я не думаю, что это окупается.
Ответ 2
Тогда почему компиляторам по-прежнему требуется ключевое слово typename?
Так как правила языка формулируются таким образом: каждое зависимое имя, которое используется в контексте типа, должно предшествовать typename
(mutatis mutandis, то же самое имеет место для имени шаблона и ключевого слова template
). Итак, почему правила языка не имеют значения между случаями, когда требуется имя типа для устранения двусмысленности и тех, где контекст предоставляет достаточную информацию? Вероятно, меня не было, когда было принято решение - чтобы содержание описания языка было еще более сложным (рассмотрите последствия недостающих случаев, так или иначе).
В вашем примере X не является именем типа (эта возможность, если для совместимости с C, где имя тега не является автоматически именем типа), поэтому вам нужно yuse struct
:
pX = new struct T::X;
Ответ 3
Ваш код, кажется, попадает в очень серая область.
Этот абзац о скрытии имени
Имя класса (9.1) или имя перечисления (7.2) можно скрыть по имени переменная, элемент данных, функция или перечислитель, объявленный в том же объеме. Если имя класса или перечисления и переменная, элемент данных, функция или перечислитель объявляется в том же (в любом порядке) с тем же имя, класс или имя перечисления скрытый везде, где переменная, данные имя члена, функции или имени счетчика.
похоже, указывает, что компилятор прав, чтобы жаловаться, что A::X
не является именем типа.