Как скрыть шаблон помощника реализации?
Предположим, что у меня есть две функции шаблона, объявленные в файле заголовка:
template <typename T> void func1(const T& value);
template <typename T> void func2(const T& value);
И предположим, что реализация этих функций (также в файле заголовка, а не в исходном файле, потому что они являются шаблонами) использует некоторую вспомогательную функцию реализации, которая также является шаблоном:
template <typename T> void helper(const T& value) {
// ...
}
template <typename T> void func1(const T& value) {
// ...
helper(value);
}
template <typename T> void func2(const T& value) {
// ...
helper(value);
}
В любом исходном файле, который я включаю в заголовочный файл, вспомогательная функция будет видна. Я не хочу этого, потому что вспомогательная функция - это просто деталь реализации. Есть ли способ скрыть вспомогательную функцию?
Ответы
Ответ 1
Общий подход (например, используемый во многих библиотеках Boost) заключается в том, чтобы помещать помощника в пространство имен, называемое details
, возможно, в отдельном заголовке (включенном в заголовок "public" ).
Там нет способа предотвратить его видимость и вызываемость, но это довольно ясно указывает, что это часть реализации, а не интерфейс.
Ответ 2
Два варианта с головы:
- Переместите всю реализацию в файл hpp, который вы укажете в нижней части h файла.
- Обновите свой код как шаблоны классов, а затем сделайте помощников конфиденциальными.
Ответ 3
Поскольку пользователю вашего кода необходимо увидеть полное определение функции func1
, его можно реализовать, а также реализовать реализацию вспомогательной функции.
Но если вы переместите реализацию в другой файл, пользователю придется столкнуться с объявлением шаблона:
//templates.h
template< typename T > void f1( T& );
#include <templates_impl.h> // post-inclusion
И определение:
// templates_impl.h
template< typename T > void f1_helper( T& ) {
}
template< typename T > void f1( T& ) {
// the function body
}
Ответ 4
Установленный прецедент заключается в том, чтобы помещать подобную вещь в специально (то есть последовательно) с именем вложенное пространство имен. Boost использует namespace details
, Loki использует namespace Private
. Очевидно, ничто не может помешать кому-либо использовать содержимое этих пространств имен, но оба названия передают значение, что их содержимое не предназначено для общего потребления.
Говоря проще, можно превратить func1
и func2
из шаблонов свободных функций в статические шаблоны функций-членов некоторого общего класса; Таким образом, helper
может просто быть частным членом указанного класса, невидимым для внешнего мира:
struct funcs {
template<typename T>
static void func1(T const& value) {
// ...
helper(value);
}
template<typename T>
static void func2(T const& value) {
// ...
helper(value);
}
private:
template<typename T>
static void helper(T const& value) {
// ...
}
};
Ответ 5
Я хотел бы (как было сказано ранее) создать класс шаблона, сделать все функции static и вспомогательную функцию приватной. Но кроме того, я бы также рекомендовал сделать конструктор закрытым, как показано ниже:
template <typename T>
class Foo{
public:
static void func1(const T& value);
static void func2(const T& value);
private:
Foo();
static void helper(const T& value);
}
Когда вы создадите конструктор private, компилятор не разрешит экземпляры этого класса шаблонов. Таким образом, следующий код станет незаконным:
#include "foo.h"
int main(){
int number = 0;
Foo<int>::func1(number); //allowed
Foo<int>::func2(number); //allowed
Foo<int>::helper(number); //not allowed, because it private
Foo<int> foo_instance; //not allowed, because it private
}
Так зачем кому-то это хотеть? Потому что наличие разных экземпляров, которые ТОЧНО, то же самое, чего вы, вероятно, никогда не захотите. Когда компилятор сообщает вам, что конструктор какого-либо класса является приватным, вы можете предположить, что наличие разных экземпляров его было бы лишним.