Понимание запутанной грамматики typedef - PullRequest
41 голосов
/ 13 декабря 2011

Рассмотрим следующий фрагмент кода

typedef int type;
int main()
{
   type *type; // why is it allowed?
   type *k ;// which type?
}

Я получаю ошибку 'k' is not declared in this scope.Компилятор анализирует type *k как умножение между type* и k.Разве эта грамматика не очень запутанная?

Почему стандарт C ++ допускает type *type?Потому что грамматика так говорит?Почему?

Ответы [ 5 ]

23 голосов
/ 13 декабря 2011
type *type; // why is it allowed?

C ++ 11 3.3.2 / 1 говорит:

Точка объявления имени находится сразу после его полного декларатора (пункт 8) и до его инициализатора (если есть)

Таким образом, имя переменной type не вводится до тех пор, пока не будет использовано имя типа type; имя типа является единственным доступным значением type во время объявления.

type *k ;// which type?

Имя локальной переменной скрывает имя глобального типа, поэтому оно выбрано здесь. Это описано в C ++ 11 3.3.10 / 1:

Имя может быть скрыто явным объявлением того же имени во вложенной декларативной области или производном классе.

Полное имя типа ::type, конечно, все еще доступно.

15 голосов
/ 13 декабря 2011

Вопрос на самом деле о том, когда именно имя переменной определяется как идентификатор, и язык определяет, что оно находится сразу после точки в коде, где объявлена ​​переменная:

typedef int type;
int main() {
   type t;   // type refers to ::type
   int       // type still refers to ::type
   type;     // variable declared, this shadows ::type
   type + 1; // type is a variable of type int.
}

Существуют аналогичные правила в других контекстах, и это просто вопрос принятия решения, когда идентификаторы объявлены. Существуют и другие подобные ситуации, например, в списке инициализации класса:

struct test {
   int x;          // declare member
   test( int x )   // declare parameter (shadows member)
   : x(            // refers to member (parameter is not legal here)
        x )        // refers to parameter
   {};
};

Или в области действия идентификаторов при определении функций-членов:

struct test {
   typedef int type;
   type f( type );
};
test::type         // qualification required, the scope of the return type is
                   // at namespace level
test::f(
         type t )  // but the scope of arguments is the class, no qualification
                   // required.
{}

Что касается обоснования решения, я не могу вам сказать, но оно последовательное и простое.

3 голосов
/ 13 декабря 2011

Это сбивает с толку, но это единственный способ получить доступ к переменной type. Если вы хотите использовать type тип, вы можете сделать:

typedef int type;
int main() {
    type *type;
    ::type *k ;
    return 0;
} 

Большинство этих грамматических уродств происходит из-за обратной совместимости с C.

1 голос
/ 13 декабря 2011

Обоснование разделения пространств имен (не в смысле C ++, а в пространстве имен переменных / типов) довольно очевидно: если вы не загрязняете пространство имен переменных именами типов, меньше кодовых разрывов в typedef.

Предположим, что ранее существовал код с переменной "employee". Если переменные и typedefs жили в одном и том же пространстве имен, то "typedef struct {} employee;" сломал бы существующий код, требуя изменения имени переменной (что было больше проблемы в дни до IDE). Однако, если они не совместно используют пространство имен, это не проблема, и у людей возникает еще одна проблема, о которой следует беспокоиться при выборе имен типов в больших базах кода.

0 голосов
/ 13 декабря 2011

Я думаю, что это возможно, вероятно, потому что это обеспечивает гибкость для программистов при выборе имени для переменных, которые они объявляют.В C # вы можете объявить свойство с тем же именем, что и тип:

//C# code
class Manager
{
   public Name Name {get;set;}
};

Когда я пишу код на C #, я нахожу эту функцию очень полезной.Потому что у меня есть больше вариантов для выбора имен.В противном случае, если у меня есть тип с именем Name, я не смог бы создать свойство с тем же именем, я бы заставил выбрать другое имя, скажем Name_,_Name, name, NAME и т. Д. - все это мне не нравится.


Что касается вашего кода, так как в области действия (после объявления object type), type уже является переменной, к типу type `нельзя обращаться напрямую.Но я думаю, что это должно скомпилироваться нормально и в соответствии со стандартом:

typedef int type;
int main()
{
   type *type; // why is it allowed?
   ::type *k ;// which type?
}

Демо: http://ideone.com/chOov

...