Понимание запутанной грамматики typedef
Рассмотрим следующий фрагмент кода
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
. Разве эта грамматика не очень запутанна?
Почему type *type
разрешен стандартом С++? Потому что грамматика так говорит? Почему?
Ответы
Ответ 1
Вопрос в том, когда именно имя переменной определяется как идентификатор, и язык определяет, что он сразу после точки в коде, где объявлена переменная:
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.
{}
С точки зрения обоснования решения я не могу вам сказать, но он последователен и прост.
Ответ 2
type *type; // why is it allowed?
С++ 11 3.3.2/1 говорит:
Точка объявления для имени сразу после его полного объявления (раздел 8) и перед его инициализатором (если есть)
Таким образом, имя переменной type
не вводится до тех пор, пока не будет использовано имя типа type
; имя типа является единственным доступным значением type
во время декларатора.
type *k ;// which type?
Локальное имя переменной скрывает глобальное имя типа, поэтому оно выбрано здесь. Это описано в С++ 11 3.3.10/1:
Имя может быть скрыто явным объявлением того же имени во вложенной декларативной области или производном классе.
Конечно, все еще доступно полное имя типа ::type
.
Ответ 3
Это запутанно, но это единственный способ получить доступ к переменной type
.
Если вы хотите использовать тип type
, вы можете сделать:
typedef int type;
int main() {
type *type;
::type *k ;
return 0;
}
Большинство этих грамматических монстров происходят из обратной совместимости с C.
Ответ 4
Обоснование сохранения пространств имен (а не в смысле С++, но в пространстве имен переменных/типов) совершенно очевидно: если вы не загрязняете пространство имен переменных именами типов, меньшее количество разрывов кода на typedef.
Предположим, что существовал ранее существовавший код с переменной с именем "employee". Если переменные и typedefs жили в одном и том же пространстве имен, "typedef struct {} employee;" будет нарушать существующий код, требуя изменения имени переменной (что было больше проблемой в дни, предшествующие IDE). Однако, если они не разделяют пространство имен, проблем нет, и у людей возникает одна проблема, о которой стоит беспокоиться при выборе имен типов в больших базовых кодах.
Ответ 5
Я думаю, что это допустимо, вероятно, потому, что он обеспечивает гибкость для программистов при выборе имени для переменных, которые они объявляют. В С# вы можете объявить свойство с тем же именем, что и тип:
//C# code
class Manager
{
public Name Name {get;set;}
};
Когда я код на С#, я считаю эту функцию очень полезной. Потому что у меня больше вариантов выбора имен. В противном случае, если у меня есть тип, называемый Name
, тогда я бы не смог создать свойство с тем же именем, я был бы вынужден выбрать другое имя, например Name_
, _Name
, Name
, Name
и т.д. - все это не нравится мне.
Что касается вашего кода, поскольку в области (после объявления объекта type
), type
уже является переменной, тип type не может быть передан напрямую. Но я думаю, что это должно составить штраф и в соответствии со стандартом:
typedef int type;
int main()
{
type *type; // why is it allowed?
::type *k ;// which type?
}
Демо: http://ideone.com/chOov