2015-10-15 1 views
2

В приведенном ниже коде 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 «s VARIADIC шаблон для работы, он должен иметь, что первоначальный вариант без аргументов. Но это заставляет меня определить функцию без шаблона - это заголовочный файл, который будет разбиваться после включения этого файла в несколько исходных файлов. Я не могу переместить определение foo_helper в исходный файл, потому что он находится в анонимном пространстве имен, поскольку он не должен быть доступен.

Есть ли способ решить эту проблему?

+0

BTW, вам нужно заменить 'foo_helper (args)' на 'foo_helper (args ...)' и 'foo_helper (tail)' с 'foo_helper (tail ...)'. – KyleKnoepfel

+0

@KyleKnoepfel Исправлено, спасибо. – Leonhart231

ответ

3
inline void foo_helper() {}; 

решает вашу проблему.

inline в основном означает, что «противоречивые определения этой функции должны быть отброшены, а одна из версий сохранена».

Он также необязательно предлагает «встраивание» в смутный способ (в том смысле, что стандарт действительно не охватывает то, что встраивается). Компиляторы могут или не могут обратить внимание на это предложение.

Обратите внимание, что анонимное пространство имен не «делает его непригодным» или что-то еще. Анонимные пространства имен предназначены для блокировки столкновений линкеров, и это все. Создайте пространство имен с именем details и ... хорошо, доверьтесь пользователям, чтобы они не заходили и не ткнули внутрь.

Использование анонимного пространства имен в заголовке является очень плохой идеей.

Если в другом заголовочном файле есть функция inline (или функция шаблона), которая обращается к символу или функции в анонимном пространстве имен, вы почти наверняка будете иметь нарушение ODR (одно правило определения). Это то, где один и тот же объект, функция и т. Д. Имеет два определения, которые отличаются и не допускаются.

Например:

inline void bob() { 
    foo(1,2,3); 
} 

если это #include d в двух разных файлах .cpp, вы просто сделали плохо сформированные программы (без диагностики не требуется).

Часто такие плохо сформированные программы «ведут себя так, как вы ожидаете», но иногда они не. В качестве примера, если где-то вдоль линии вы получаете локальную переменную static, существование которой зависит от нарушения ODR, вы можете иметь несколько единиц компиляции не согласны с тем, что существует и каковы его свойства.

В более общем смысле, порядок ссылок вашей программы может изменить свое поведение, поскольку различные определения «выбраны» (возможно, чрезвычайно тонкие различия). Или фаза Луны может сделать то же самое.

Нарушения ODR на удивление доброкачественны, пока они не бают вас с нелокальными ошибками, которые трудно отследить.

+0

Это решает этот конкретный случай для меня. Но есть ли способ сделать эту работу вообще, когда 'inline' будет по какой-то причине нежелательным? – Leonhart231

+0

@ Leonhart231 'inline' не означает, что вы думаете, что это значит? – Yakk

+0

Вы снова верны. Я привык к встроенным платформам, где 'inline' буквально означает inline. Спасибо за помощь! И спасибо @coppro за то, что напомнили мне. – Leonhart231

1

Я начну с рассмотрения: использование анонимного пространства имен здесь не служит вашей цели. Поскольку вы определяете его в заголовочном файле, он вообще не защищен: он все равно будет отображаться в любом файле, который содержит ваш заголовок. Кроме того, поскольку вы определили его в анонимном пространстве имен, отдельная копия функции будет испускаться в каждой единицы перевода, которая ее использует, и компоновщик не может свернуть их. Если вы действительно хотите, чтобы он был закрытым, я не нахожусь в верхней части лучшего стиля C++ в наши дни, поэтому, возможно, кто-то меня поправит, но я бы склонен использовать личное пространство имен:

namespace my_stuff { 
    void foo_helper(); 
} 

void foo() { 
    my_stuff::foo_helper(); 
} 

Как указано в Yakk, вы можете использовать встроенную функцию, и это позволит компилятору свернуть определения в один. В современной практике на самом деле не должно быть другой причины избегать ключевого слова inline, потому что теперь компиляторы сами решат, использовать ли встроенные функции, а не слушать подсказки, которые вы даете.

Поскольку вы определили функцию в анонимном пространстве имен, как я уже упоминал выше, вам действительно не нужно ничего делать, чтобы избежать ошибок компоновщика, если вы сохраните это. Недостатком этого подхода является то, что у вас будут отдельные копии foo_helper() в каждой единицы перевода, и они не могут быть объединены компоновщиком.

Есть еще одна гимнастика, которую вы можете сделать, в основном с участием sizeof..., но я не думаю, что они идеальны.

+0

Это хорошая идея об анонимном пространстве имен. Я использовал их только в исходных файлах, поэтому я не думал об этом до конца. Здесь много хороших моментов, поэтому спасибо. – Leonhart231

Смежные вопросы