在下面的代码中,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文件中是 #include
d,那么您只是创建了一个格式错误的程序(不需要诊断)。
通常这种病态的程序"按照您期望的方式运行",但有时它们不。例如,如果您在某个地方得到一个static
局部变量,它的存在依赖于ODR冲突,那么您可以让多个编译单元不同意哪个存在以及它的属性是什么。
在更一般的意义上,程序的链接顺序可能会改变它的行为,因为"选择"了不同的定义(可能有非常细微的差异)。或者月亮的相位也可以做同样的事情。
违反ODR出奇地温和,直到它们给您带来难以追踪的非本地错误。
我先说句题外话:在这里使用匿名名称空间不符合您的目的。由于您是在头文件中定义它的,因此它根本不受保护:它仍然在任何包含您的头文件的作用域中。此外,由于您在匿名名称空间中定义了它,因此将在使用它的每个翻译单元中发出该函数的单独副本,并且链接器无法折叠它们。如果你真的希望它是私有的,我现在并不是最好的c++风格,所以也许其他人会纠正我,但我倾向于使用私有命名空间:
namespace my_stuff {
void foo_helper();
}
void foo() {
my_stuff::foo_helper();
}
正如Yakk所指出的,您可以使用内联函数,这将允许编译器将定义折叠成一个。在现代实践中,应该没有其他理由避免使用inline
关键字,因为编译器现在会自己决定是否使用内联函数,而不是听你给的提示。
既然在匿名命名空间中定义了函数,正如我上面提到的,如果保留了这个名称空间,实际上不需要做任何其他事情来避免链接器错误。这种方法的缺点是,您将在每个翻译单元中拥有单独的foo_helper()
副本,并且这些副本不能被链接器合并。
还有其他体操你可以做,主要涉及sizeof...
,但我不认为这些是理想的。