В приведенном ниже коде foo
должна быть доступной для всех функцией, но foo_helper
не должен, поэтому я поместил его в анонимное пространство имен. Очевидно, что я ухожу от охраны и включает в этом примере, но они есть.
foo.h
:
namespace
{
void foo_helper() {}
template <typename T, typename... Tail>
void foo_helper(T head, Tail... tail)
{
bar(head);
foo_helper(tail...);
}
}
void foo();
template <typename... Args>
void foo(Args... args)
{
before();
foo_helper(args...);
after();
}
foo.cpp
:
void foo() {}
Проблема в том, что для foo_helper
Вариативный шаблон для работы, он должен иметь эту начальную версию без аргументов. Но это вынуждает меня определить не шаблонную функцию как заголовочный файл, который сломался бы после включения этого файла в несколько исходных файлов. Я не могу переместить определение foo_helper
в исходный файл, потому что он находится в анонимном пространстве имен, так как он не должен быть доступным.
Есть ли способ решить эту проблему?
inline void foo_helper() {};
решает вашу проблему
inline
в основном означает, что «противоречивые определения этой функции должны быть отброшены, а одна из версий сохранена».
Это также необязательно предполагает «встраивание» в расплывчатом виде (в том смысле, что стандарт на самом деле не охватывает, что такое встраивание). Составители могут обратить или не обратить внимание на это предложение.
Обратите внимание, что анонимное пространство имен не «делает его непригодным для использования» или чем-то еще. Анонимные пространства имен предназначены для блокировки конфликтов компоновщика, и это все. Создайте пространство имен с именем details
и … хорошо, доверяйте пользователям, чтобы они не заходили и не совали их внутрь.
Использование анонимного пространства имен в заголовке очень плохая идея.
Если есть inline
Функция (или функция шаблона) в другом заголовочном файле, который обращается к символу или функции в анонимном пространстве имен, вы почти наверняка будете иметь нарушение ODR (одно правило определения). Вот где один и тот же объект, функция и т. Д. Имеют два определения, которые различаются и не допускаются.
Например:
inline void bob() {
foo(1,2,3);
}
если это #include
d в двух разных файлах .cpp, вы только что создали плохо сформированную программу (диагностика не требуется).
Часто такие плохо сформированные программы «ведут себя так, как вы ожидаете», но иногда они не. Например, если где-то вдоль линии вы получите static
Локальная переменная, существование которой зависит от нарушения ODR, может иметь несколько модулей компиляции, которые не согласны с тем, какая существует и каковы ее свойства.
В более общем смысле порядок ссылок вашей программы может изменить ее поведение, поскольку «выбраны» разные определения (возможно, с очень тонкими различиями). Или фаза луны может сделать то же самое.
Нарушения ODR удивительно доброкачественны, пока они не блокируют вас нелокальными ошибками, которые трудно отследить.
Я начну с отступления: использование анонимного пространства имен здесь не соответствует вашей цели. Так как вы определяете его в заголовочном файле, он вообще не защищен: он все равно будет находиться в области видимости любого файла, который включает ваш заголовок. Кроме того, поскольку вы определили его в анонимном пространстве имен, отдельная копия функции будет передаваться в каждом модуле перевода, который ее использует, и компоновщик не может их свернуть. Если вы действительно хотите, чтобы он был приватным, я не в курсе лучших стилей C ++ в наши дни, так что, возможно, кто-то еще исправит меня, но я бы склонен использовать личное пространство имен:
namespace my_stuff {
void foo_helper();
}
void foo() {
my_stuff::foo_helper();
}
Как указал Yakk, вы можете использовать встроенную функцию, и это позволит компилятору свести определения в единое целое. В современной практике не должно быть другой причины избегать inline
ключевое слово, потому что в настоящее время компиляторы сами решают, использовать встроенные функции или нет, а не прислушиваться к подсказкам, которые вы даете.
Поскольку вы определили функцию в анонимном пространстве имен, как я уже упоминал выше, вам на самом деле не нужно ничего делать, чтобы избежать ошибок компоновщика, если вы сохраните это. Недостатком этого подхода является то, что у вас будут отдельные копии foo_helper()
в каждой единице перевода, и те не могут быть объединены компоновщиком.
Есть другие виды гимнастики, которые вы можете сделать, в основном с участием sizeof...
Но я не думаю, что они идеальны.