Пространства имен в С++ и c
Не то, чтобы я когда-либо писал свой код в своей профессиональной работе, следующий код является законным и компилируется без предупреждений в С++ и c:
#include <stdlib.h>
typedef struct foo { int foo; } foo;
foo * alloc_foo () {
return (struct foo*) malloc(sizeof(foo));
}
struct foo * alloc_struct_foo () {
return (foo*) malloc(sizeof(struct foo));
}
foo * make_foo1 (int val) {
foo * foo = alloc_struct_foo ();
foo->foo = 0;
return foo;
}
struct foo * make_foo2 (int val) {
struct foo * foo = alloc_foo();
foo->foo = 0;
return foo;
}
Что делает этот законный и однозначный в C раздел 6.2.3 стандарта C:
6.2.3 Пространства имен идентификаторов
Если в какой-либо точке единицы перевода видится более одного объявления определенного идентификатора, синтаксический контекст устраняет неоднозначность использования, относящегося к разным объектам. Таким образом, существуют отдельные пространства имен для различных категорий идентификаторов (имена меток, теги структур, союзов и перечислений, члены структур или союзов и обычные идентификаторы).
Обратите внимание, что благодаря именам ярлыков, живущих в их собственных пространствах имен, я мог бы сделать код еще более запутанным, используя метку foo
где-то.
Добавьте следующее, и код не скомпилируется:
int foo (foo * ptr) {
return ++ptr->foo;
}
Итак, два вопроса, один из которых связан с C и С++, а другой - с С++.
-
Вопрос C/С++: Почему я не могу определить функцию foo
?
Кажется, я должен был бы определить функцию foo
; имена функций и имена переменных являются "обычными идентификаторами". Но если я добавлю этот последний бит кода, я получаю error: redefinition of 'foo' as different kind of symbol
.
Вопрос: foo * foo;
является совершенно законным, поэтому почему int foo (foo*);
не закончен?
-
Вопрос на С++: как это вообще работает на С++?
Значение "пространства имен" в С++ имеет иное значение, чем в C. Я не могу найти ничего в стандарте С++, который говорит о понятии C пространства имен, что и делает законным в C.
Вопрос: Что делает этот законным в С++ (предпочтительнее глава и стих)?
Ответы
Ответ 1
foo * foo;
является совершенно законным, так почему же не int foo (foo*);
legal?
Потому что уже существует тип с именем foo
в том же контексте объявления, что и ваша функция. Вы не можете иметь тип и функцию с тем же именем в той же области.
Как это вообще работает на С++?
Поскольку вам разрешено скрывать имена во вложенных областях. Когда вы объявляете foo * foo
, первый foo
относится к типу. Второй foo
объявляет переменную - в этой точке тип foo
скрыт. Попробуйте объявить foo * baz
после foo * foo
, он должен потерпеть неудачу.
struct foo {};
void test() {
foo * foo; // first `foo` is the type, second `foo` is the variable
foo * baz; // first `foo` is the variable
}
Ответ 2
В С++ 11 3.3.1/4 говорит, что в декларативной области все объявления имени должны ссылаться на один и тот же объект (или набор перегрузки). Существует исключение, которое позволяет использовать имя класса для набора имен функций (поэтому foo()
hides class foo
), но это не применяется, если у вас есть typedef (что вы делаете).
Попробуйте с typedef struct foo foo
, опущенным в С++.
Ответ 3
Это тоже не работает на С++... проблема в том, что предварительный процессор для gcc/g++ ищет __cplusplus
, а не cplusplus
. Поэтому вы предпроцессорных операторов
#if defined FOO && ! defined cplusplus
#undef FOO
#endif
работают неправильно.
Ответ 4
Поскольку уже существует функция foo()
, это конструктор по умолчанию для struct foo
typedef struct foo
{
int a;
foo(int val)
:a(val)
{}
} foo;
int foo(int value)
{
cout << value <<endl;
}
void main()
{
foo foovar = foo(50); // constructor foo or function foo?
}
В C не существует таких объектов, как конструкторы.
Редактировать специально для Алана Стокса:
typedef struct foo
{
int a;
foo(int val, double val2)
:a(val)
{
cout << val2 << endl;
}
} foo;
int foo(int value, double val2)
{
cout << value << val2 << endl;
}
void main()
{
some_random_func(foo(50, 1.0)); // constructor foo or function foo?
}