如何在匿名命名空间中调用可变参数模板助手?

时间:2015-10-15 17:12:41

标签: c++ c++11 variadic-templates unnamed-namespace

在下面的代码中,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的可变参数模板起作用,它需要具有不带参数的初始版本。但是,这迫使我定义一个非模板函数是一个头文件,在将该文件包含在多个源文件中之后会中断。我无法将foo_helper的定义移动到源文件,因为它位于匿名命名空间中,因为它不应该是可访问的。

有没有办法解决这个问题?

2 个答案:

答案 0 :(得分:3)

inline void foo_helper() {};

解决您的问题。

inline主要意味着&#34;此功能的冲突定义将被丢弃,其中一个版本保留&#34;。

它也非约束性地暗示&#34;内联&#34;以一种模糊的方式(因为标准并没有涵盖内联的内容)。编制者可能会也可能不会关注该建议。

请注意,匿名命名空间不会使其无法使用&#34;管他呢。匿名命名空间旨在阻止链接器冲突,这就是它。创建一个名为details的命名空间,并且......好吧,相信用户不要去捅内部。

在标题中使用匿名命名空间非常糟糕

如果在另一个头文件中有inline函数(或模板函数)访问匿名命名空间中的符号或函数,则几乎肯定会出现ODR(一个定义规则)违规。这就是同一个对象,函数等有两个不同的定义,并且不允许这样做。

例如:

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

如果在两个不同的.cpp文件中只有#include d,那么您只是制作了一个格式错误的程序(无需诊断)。

这些形成错误的程序往往表现得像你期望的那样#34;但有时他们不会。例如,如果沿着这一行的某个地方你得到一个static局部变量,它的存在取决于ODR违规,你可以让多个编译单元不同意哪个存在以及它的属性是什么。

从更一般的意义上讲,程序的链接顺序可能会改变其行为,因为选择了不同的定义&#34; (可能有极其微妙的差异)。或者月相可以做同样的事情。

ODR违规是令人惊讶的良性,直到他们用难以追踪的非本地错误来代替您。

答案 1 :(得分:1)

我将从一开始就说:在这里使用匿名命名空间并不能满足您的需求。由于您在头文件中对其进行了定义,因此它根本不受保护:它仍然在包含您的标头的任何文件的范围内。此外,由于您已在匿名命名空间中定义了该函数,因此将在使用该函数的每个转换单元中发出该函数的单独副本,并且链接器无法折叠它们。如果你真的希望它是私有的,那么我现在没有最好的C ++风格,所以也许其他人会纠正我,但我倾向于使用私有命名空间:< / p>

namespace my_stuff {
    void foo_helper();
}

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

正如Yakk指出的那样,你可以使用内联函数,这将允许编译器将定义折叠成一个。在现代实践中,避免使用inline关键字不应该是另一个原因,因为编译器现在会自行决定是否内联函数而不是听你提供的提示。

由于您已在匿名命名空间中定义了该函数,如上所述,如果您保留链接器错误,则实际上不需要执行任何其他操作来避免链接器错误。这种方法的缺点是,每个翻译单元中都会有foo_helper()的单独副本,而这些副本不能被链接器合并。

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