假设我在头文件中声明了两个模板函数:
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);
}
在我包含头文件的任何源文件中,辅助函数都是可见的。我不希望这样,因为辅助函数只是一个实现细节。有没有办法隐藏辅助功能?
答案 0 :(得分:20)
一种常见的方法(例如,在许多Boost库中使用)是将帮助器放在名为details
的名称空间中,可能位于单独的标头中(包含在&#34; public&#34;标头中) )。
没有办法阻止它可见和可调用,但这清楚地表明它是实现的一部分,而不是接口。
答案 1 :(得分:4)
我头顶的两个选项:
答案 2 :(得分: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
}
答案 3 :(得分:3)
既定的先例是将这种事物放在一个特殊的(即一致的)命名嵌套命名空间中。 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) {
// ...
}
};
答案 4 :(得分:1)
我会(如前所述)创建一个模板类,使所有函数保持静态,并将辅助函数设为私有。但除此之外,我还建议将构造函数设为私有,如下所示:
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);
}
当您将构造函数设为私有时,编译器将不允许此模板类的实例。所以下面的代码将变成非法的:
#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's private
Foo<int> foo_instance; //not allowed, because it's private
}
那么为什么会有人想要这个呢?因为具有完全相同的不同实例是您可能永远不会想要的。当编译器告诉你某个类的构造函数是私有的时,你可以假设它的不同实例是不必要的。
答案 5 :(得分:1)
在 C++20 中,您现在可以使用 modules。
为此,您可以创建一个模块 some_module.cppm
保存所有函数并仅标记接口(单个函数或命名空间)使用 export
而不暴露辅助函数:
// some_module.cppm
export module some_module;
template <typename T>
void helper(const T& value) {
// ...
}
export
template <typename T>
void func1(const T& value) {
// ...
helper(value);
}
export
template <typename T>
void func2(const T& value) {
// ...
helper(value);
}
在上面的例子中,只有函数 func1
和 func2
被导出并且可以在 main.cpp
中访问:
// main.cpp
#include <cstdlib>
import some_module;
int main() {
func1(1.0);
func2(2.0);
return EXIT_SUCCESS;
}
在 clang++12
中,您可以使用以下三个命令编译此代码:
clang++ -std=c++2b -fmodules-ts --precompile some_module.cppm -o some_module.pcm
clang++ -std=c++2b -fmodules-ts -c some_module.pcm -o some_module.o
clang++ -std=c++2b -fmodules-ts -fprebuilt-module-path=. some_module.o main.cpp -o main
./main
答案 6 :(得分:1)
我知道你的意思是你想把它隐藏得非常好,只要调用者不改变你的代码文件,他们就无法找到你的辅助函数。我非常了解它,因为我最近有非常相似的需求。
那么如何将您的辅助函数包装在匿名命名空间中?这是最近发现的最优雅的款式:
namespace YourModule
{
namespace
{//Your helper functions}
//Your public functions
}
这种做法有效地对外部隐藏了您的内部功能。我找不到调用者可以访问匿名命名空间中的函数的任何方式。
正如@user3635700 所回答的那样,将命名空间转换为充满静态函数的类通常不是一个好习惯,尤其是当您有静态变量模板时,例如:
template <uint8_t TimerCode>
static uint16_t MillisecondsElapsed;
如果这个变量出现在一个类中,你必须在类之外的某个地方初始化它!但是,你不能这样做,因为它是一个模板!跆拳道!
头文件中的匿名命名空间似乎被一些人批评,是满足我们需求的唯一也是最完美的解决方案。