Есть ли способ вставить глобальное имя в пространство имен и сделать его доступным только из этого пространства имен?
Скажем, у меня есть функция с привязкой "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); }
}