如何在匿名命名空间中调用可变变量模板帮助器



在下面的代码中,foo应该是任何人都可以访问的函数,但foo_helper不应该,这就是为什么我把它放在一个匿名名称空间中。显然,我在这个例子中省略了include和include,但它们确实存在。

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

如果在两个不同的.cpp文件中是 #included,那么您只是创建了一个格式错误的程序(不需要诊断)。

通常这种病态的程序"按照您期望的方式运行",但有时它们不。例如,如果您在某个地方得到一个static局部变量,它的存在依赖于ODR冲突,那么您可以让多个编译单元不同意哪个存在以及它的属性是什么。

在更一般的意义上,程序的链接顺序可能会改变它的行为,因为"选择"了不同的定义(可能有非常细微的差异)。或者月亮的相位也可以做同样的事情。

违反ODR出奇地温和,直到它们给您带来难以追踪的非本地错误。

我先说句题外话:在这里使用匿名名称空间不符合您的目的。由于您是在头文件中定义它的,因此它根本不受保护:它仍然在任何包含您的头文件的作用域中。此外,由于您在匿名名称空间中定义了它,因此将在使用它的每个翻译单元中发出该函数的单独副本,并且链接器无法折叠它们。如果你真的希望它是私有的,我现在并不是最好的c++风格,所以也许其他人会纠正我,但我倾向于使用私有命名空间:

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

正如Yakk所指出的,您可以使用内联函数,这将允许编译器将定义折叠成一个。在现代实践中,应该没有其他理由避免使用inline关键字,因为编译器现在会自己决定是否使用内联函数,而不是听你给的提示。

既然在匿名命名空间中定义了函数,正如我上面提到的,如果保留了这个名称空间,实际上不需要做任何其他事情来避免链接器错误。这种方法的缺点是,您将在每个翻译单元中拥有单独的foo_helper()副本,并且这些副本不能被链接器合并。

还有其他体操你可以做,主要涉及sizeof...,但我不认为这些是理想的。

相关内容

  • 没有找到相关文章

最新更新