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

Использование ключевого слова 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. Пожалуйста, взгляните на него перед ответом.: -)

4b9b3361

Ответ 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 не является именем типа.