如何隐藏实现帮助程序模板?

发布于 2024-11-04 14:43:24 字数 589 浏览 0 评论 0原文

假设我在头文件中声明了两个模板函数:

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);
}

在我包含头文件的任何源文件中,辅助函数都将可见。我不希望这样,因为辅助函数只是一个实现细节。有没有办法隐藏辅助功能?

Suppose that I have two template functions declared in a header file:

template <typename T> void func1(const T& value);
template <typename T> void func2(const T& value);

And suppose that the implementation of these functions (also in a header file and not in a source file, because they are templates) uses some implementation helper function, which is also a template:

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);
}

In any source file that I include the header file, the helper function will be visible. I don't want that, because the helper function is just an implementation detail. Is there a way to hide the helper function?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

小红帽 2024-11-11 14:43:24

一种常见的方法(例如,在许多 Boost 库中使用)是将帮助程序放在名为 details 的命名空间中,可能在单独的标头中(包含在“public”标头中)。

没有办法阻止它可见和可调用,但这非常清楚地表明它是实现的一部分,而不是接口的一部分。

A common approach (as used in many Boost libraries, for example) is to put the helper in a namespace called details, possibly in a separate header (included from the "public" header).

There's no way to prevent it from being visible, and callable, but this quite clearly indicates that it is part of the implementation, not the interface.

回忆追雨的时光 2024-11-11 14:43:24

既定的先例是将此类事物放入专门(即一致)命名的嵌套命名空间中。 Boost 使用命名空间详细信息,Loki 使用私有命名空间。显然,没有什么可以阻止任何人使用这些命名空间的内容,但这两个名称都传达了这样的含义:它们的内容不适合一般消费。

话虽这么说,一个简单的替代方案是将 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) {
        // ...
    }
};

The established precedent is to put that sort of thing in a specially (i.e. consistently) named nested namespace. Boost uses namespace details, Loki uses namespace Private. Obviously nothing can prevent anyone from using the contents of those namespaces, but both names convey the meaning that their contents aren't intended for general consumption.

That being said, an easy alternative is to turn func1 and func2 from free function templates into static member function templates of some common class; this way, helper can simply be a private member of said class, invisible to the outside world:

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) {
        // ...
    }
};
你怎么敢 2024-11-11 14:43:24

我想到了两个选项:

  1. 将所有实现移至 hpp 文件中,该文件包含在 h 文件的底部。
  2. 将代码重构为类模板,然后将助手设为私有。

Two options off the top of my head:

  1. Move all the implementation to an hpp file which you include at the bottom of your h file.
  2. Refactor your code as class templates, then make the helpers private.
别低头,皇冠会掉 2024-11-11 14:43:24

C++20中,您现在可以使用模块
为此,您可以创建一个模块 some_module.cppm 保存所有函数并仅标记接口(单个函数或命名空间)使用 export< /code>,同时不公开辅助函数:

// 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);
}

在上述情况下,仅导出函数 func1func2 并可在 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

In C++20 you can now use modules.
For this you could create a module some_module.cppm holding all functions and marking only the interface (either the individual functions or a namespace) with export while not exposing the helper functions:

// 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);
}

In the case above only the functions func1 and func2 are exported and can be accessed inside main.cpp:

// main.cpp
#include <cstdlib>
import some_module;

int main() {
  func1(1.0);
  func2(2.0);
  return EXIT_SUCCESS;
}

In clang++12 you can compile this code with the following three commands:

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
信愁 2024-11-11 14:43:24

由于代码的用户需要查看 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
}

Since the user of your code needs to see the full definition of the func1 function, its implementation, nor its helper function implementation, cannot be hidden.

But if you move the implementation into another file, the user will only have to be confronted with the template declaration:

//templates.h
template< typename T > void f1( T& );

#include <templates_impl.h> // post-inclusion

And the definition:

// templates_impl.h
template< typename T > void f1_helper( T& ) {
}

template< typename T > void f1( T& ) {
   // the function body
}
时光清浅 2024-11-11 14:43:24

我会(如前所述)创建一个模板类,将所有函数设为静态,并将辅助函数设为私有。但除此之外,我还建议将构造函数设为私有,如下所示:

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
}

那么为什么有人想要这个呢?因为您可能永远不希望拥有完全相同的不同实例。当编译器告诉您某个类的构造函数是私有的时,您可以假设没有必要拥有它的不同实例。

I would (as said before) make a template class, make all functions static and the helper function private. But besides that I'd also recommend making the constructor private as shown below:

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);
}

When you make the constructor private, the compiler won't allow instances of this template class. So the code below would become illegal:

#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
}

So why would someone want this? Because having different instances that are EXACTLY the same is something you probably never want. When the compiler tells you that the constructor of some class is private, then you can assume that having different instances of it would be unnecesarry.

自在安然 2024-11-11 14:43:24

我知道你的意思是你想将它隐藏得如此精细,以至于调用者只要不更改你的代码文件就无法找到你的辅助函数。我非常了解这一点,因为我最近也有非常相似的需求。

那么,将辅助函数包装在匿名命名空间中怎么样?这是我最近发现的最优雅的风格:

namespace YourModule
{
namespace
{//Your helper functions}
//Your public functions
}

这种做法有效地从外部隐藏了你的内部功能。我找不到调用者可以访问匿名命名空间中的函数的任何方法。

正如 @user3635700 所回答的,将命名空间转换为充满静态函数的类通常不是一个好习惯,特别是当您有静态变量的模板时,例如:

template <uint8_t TimerCode>
static uint16_t MillisecondsElapsed;

如果此变量出现在类中,则必须初始化它课堂之外的某个地方!但是,您不能这样做,因为它是模板!卧槽!

看来,被一些人诟病的头文件中的匿名命名空间是唯一也是最完美的解决方案,可以满足我们的需求。

I know you mean that you want to hide it so finely that callers have no way to find your helper functions as long as they don't change your code file. I know it so well because I have very similar needs recently.

So how about wrapping your helper functions in an anonymous namespace? This is recently the most elegant style I found:

namespace YourModule
{
namespace
{//Your helper functions}
//Your public functions
}

This practice effectively hides your internal functions from the outside. I can't find any way that a caller can access functions in anonymous namespaces.

It's usually not a good practice, as answered by @user3635700 , to convert your namespace to a class full of static functions, especially when you have templates of static variables like:

template <uint8_t TimerCode>
static uint16_t MillisecondsElapsed;

If this variable appears in a class, you'll have to initialize it somewhere outside the class! However, you can't do it because it's a template! WTF!

It seems that an anonymous namespace in the header file, which is criticized by some, is the only and the most perfect solution to our needs.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文