Пространства имен в С++ и 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?
    }