Есть ли способ вставить глобальное имя в пространство имен и сделать его доступным только из этого пространства имен?

Скажем, у меня есть функция с привязкой "C" в глобальной области.

extern "C" int fun(int);

Затем, если я хочу сделать его видимым в пространстве имен, я бы сделал:

namespace foo {
   using ::fun;
}

Но после этого я все еще могу назвать его ::fun(0) в дополнение к foo::fun(0).

Итак, мой вопрос заключается в том, есть ли способ запретить вызов из глобального пространства имен для функции fun и разрешить его только из namespace foo?

Ответы

Ответ 1

Поскольку язык C не имеет пространств имен, часть спецификатора extern "C" указывает, что имя источника не имеет пространства имен.

Таким образом, следующий код будет вызывать функцию C foo:

namespace bar
{
    extern "C" void foo();
}

void foo() {}  // note: legal, so no pre-existing ::foo()

void caller() {
    bar::foo();  // calls the C function
}

Итак, вы могли просто сделать

namespace bar
{
    extern "C"
    {
        #include "foo.h"
    }
}

или если вам нужно поделиться им с кодом C:

#ifdef __cplusplus
namespace bar
{
    extern "C"
    {
#endif
// .. C-compatible definitions etc
#ifdef __cplusplus
    }
}
#endif

Ответ 2

В стандарте четко указывается, что внешняя функция C объявляется в пространстве имен, даже если C не знает пространства имен:

7.5/4: Спецификация привязки не устанавливает область действия. Спецификация привязки должна выполняться только в области пространства имен.

Итак, вместо объявления функции в глобальном пространстве имен вы вполне можете определить ее непосредственно в foo namespace:

// no declaration in global namespace, but... 
namespace foo {
    extern "C" int fun();
}

Затем вы можете обратиться к этой функции только через пространство имен:

foo::fun();  // yes !
::fun();     // doesn't compile !!!

Обратите внимание, что вы можете даже объявить внешнюю функцию C в нескольких пространствах имен. Все они относятся к одной и той же функции C:

namespace bar {
    extern "C" int fun();
}
...
foo::fun();  // ok
bar::fun();  // ok - same result as foo::fun(); 

Это гарантируется стандартом:

7.5/6: Не более одной функции с определенным именем может иметь C-языковую связь. Два объявления для функции с языком C связь с тем же именем функции (игнорирование имен пространства имен, которые укажите его), которые отображаются в разных областях пространства имен, относятся к той же функции.

Обратите внимание, что если функция extern была объявлена ​​в глобальном пространстве имен в одном блоке компиляции из-за определенных ограничений, вы все равно можете организовать, что это объявление не будет видно в других единицах компиляции, где вы будете использовать свое местное пространство имен. Это совершенно верно в соответствии со стандартным выражением выше. Однако, если вы играете с различной видимостью в разных единицах компиляции, вам потребуется помощь!

Ответ 3

Вы можете сделать это, так как С++ 11 с помощью объявления deleted неоднозначной функции в анонимном встроенное пространство имен:

inline namespace { int fun(int) = delete; }

Попытка вызова ::fun(0) или fun(0) приведет к ошибке:

error: call of overloaded 'fun(int)' is ambiguous
note: candidate: int fun(int)
 extern "C" int fun(int);
                ^~~
note: candidate: int {anonymous}::fun(int) <deleted>
 inline namespace { int fun(int) = delete; }
                        ^~~

Пример.

Ответ 4

Вы не можете запретить использование явного преднамеренного (неправильного) использования, но вы можете скрыть его в отдельном файле реализации, чтобы затруднить его случайное использование:

В заголовке объявите обертку:

namespace foo
{
    int fun(int);
}

Затем в одном отдельном исходном файле вы завершаете его:

extern "C" int fun(int);

namespace foo
{
    int fun(int v) { return ::fun(v); }
}