Использование ключевого слова typename с typedef и new - PullRequest
20 голосов
/ 12 декабря 2010

Рассмотрим этот код,

template<class T>
struct Sample
{ 
     typename T::X *x; //declare pointer to T's 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 достаточно ясно дают понять компилятору, что все, что следует, это type , not value,

Итак, мой вопрос: зачем компиляторам все еще нужно ключевое слово typename, даже в однозначных ситуациях, например, когда мы используем typedef и new?


РЕДАКТИРОВАТЬ (после прочтения этого ответа от Йоханнес Шауб ):

//typedef NOT followed by a type!
int typedef A;

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

Учтите это,

T::X typedef *x;

Поэтому из контекста все еще достаточно ясно, что T :: X является типом, независимо от того, появляется ли он до typedef или после typedef. Если только C ++ не позволяет нам писать typedef 5 five или typedef T::value t_value (где T :: value value ), наличие 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. Пожалуйста, посмотрите на это, прежде чем ответить. : -)

Ответы [ 3 ]

14 голосов
/ 12 декабря 2010

С ++ синтаксис более безумный, чем этот.

// typedef NOT followed by a type!
int typedef A;

// new NOT followed by a type!
new (0) int;

Другие прокомментировали ваш пример. Спецификатор typename не поддается поиску, игнорируя нетипичные имена. Поэтому, если вы скажете new typename T::X, а в T есть имя объекта X, оно все равно будет найдено вместо имени типа 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. Внутри классов интерпретация немного отличается, но очень похожа на это. Это простой и эффективный анализ.

Тот же аргумент верен и для новых.

Я склонен согласиться с вами здесь. Синтаксически это должно быть однозначно , если оставить без скобок. Поскольку я никогда не писал парсер C ++, могут быть скрытые подводные камни, которых я не вижу.

Каждое добавление typename в угловых случаях языка, как в new, потенциально потребует значительного объема проектирования как для компиляторов, так и для разработчиков стандартов, но все еще требует typename для подавляющего большинства других случаев, когда это необходимо , Я не думаю, что это окупается.

0 голосов
/ 12 декабря 2010

Ваш код выглядит как очень серая область.

Этот абзац скрывает имя

Имя класса (9.1) или имя перечисления (7.2) можно скрыть с помощьюимя переменной, члена данных, функции или перечислителя, объявленного в той же области видимости.Если имя класса или перечисления и переменная, член данных, функция или перечислитель объявляются в одной и той же области (в любом порядке) с одинаковым именем, имя класса или перечисления скрывается везде, где переменная, член данных, функция илиимя перечислителя является видимым.

, по-видимому, указывает на то, что компилятор вправе жаловаться на то, что A::X не является именем типа.

0 голосов
/ 12 декабря 2010

Тогда зачем компиляторам все еще нужно ключевое слово typename?

Поскольку правила языка сформулированы таким образом: каждому зависимому имени, используемому в контексте типа, должно предшествоватьtypename ( mutatis mutandis , то же самое относится и к имени шаблона и template ключевому слову).Теперь, почему правила языка не делают различий между случаями, когда типовое имя необходимо для устранения неоднозначности, и случаями, когда контекст предоставляет достаточно информации?Возможно - меня там не было, когда было принято решение - чтобы описание языка было еще более сложным (рассмотрите последствия пропущенных случаев, так или иначе).

В вашем примере,X не является именем типа (такая возможность, если для совместимости с C, где имя тега не является автоматически именем типа), поэтому вам нужно yuse struct:

pX = new struct T::X;
...