在下面的代码中,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
的定义移动到源文件,因为它位于匿名命名空间中,因为它不应该是可访问的。
有没有办法解决这个问题?
答案 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...
,但我不认为这些是理想的。