如何在匿名命名空间中调用可变参数模板助手?
How to call a variadic template helper in anonymous 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
的定义移动到源文件中,因为它位于匿名命名空间中,因为它不应该是可访问的。
有办法解决这个问题吗?
inline void foo_helper() {};
解决了您的问题。
inline
主要是指 "conflicting definitions of this function are to be discarded, and one of the versions kept".
它还 non-bindingly 以模糊的方式建议 "inlining"(因为该标准并未真正涵盖内联是什么)。编译器可能会或可能不会注意该建议。
请注意,匿名命名空间不会 "make it unusable" 或其他任何东西。匿名命名空间旨在阻止 linker 冲突,仅此而已。创建一个名为 details
的命名空间,然后......好吧,相信用户不要去往里面戳。
在 header 中使用匿名命名空间是一个非常糟糕的主意。
如果另一个 header 文件中有一个 inline
函数(或模板函数)访问匿名命名空间中的符号或函数,您几乎肯定会有一个 ODR(一个定义规则)违规。这就是相同的 object、函数等有两个不同的定义,这是不允许的。
例如:
inline void bob() {
foo(1,2,3);
}
如果那是 #include
d 在两个不同的 .cpp 文件中,您刚刚制作了一个 ill-formed 程序(不需要诊断)。
这种格式错误的程序通常"behave the way you expect",但有时它们不会。例如,如果沿线某处你得到一个 static
局部变量,其存在取决于 ODR 违规,你可以让多个编译单元不同意哪个存在以及它的属性是什么。
从更一般的意义上讲,程序的 link 顺序可能会改变其行为,因为不同的定义是 "chosen"(可能存在极其细微的差异)。或者月相也可以做同样的事情。
ODR 违规出人意料地良性,直到它们用 non-local 难以追踪的错误来攻击你。
我将从旁白开始:在这里使用匿名名称空间不符合您的目的。由于您在头文件中定义它,因此它根本不受保护:它仍然在包含您的头文件的任何文件的范围内。此外,由于您已在匿名命名空间中定义它,因此每个使用它的翻译单元都会发出该函数的单独副本,并且链接器无法折叠它们。如果您真的希望它是私有的,那么这些天我并不是最好的 C++ 风格的升级者,所以也许其他人会纠正我,但我倾向于使用私有命名空间:
namespace my_stuff {
void foo_helper();
}
void foo() {
my_stuff::foo_helper();
}
正如 Yakk 所指出的,您可以使用内联函数,这将允许编译器将定义折叠为一个。在现代实践中,实际上不应该有另一个理由来避免使用 inline
关键字,因为现在编译器将自行决定是否内联函数,而不是听取您提供的提示。
由于您已经在匿名命名空间中定义了函数,正如我上面提到的,如果您保留它,您实际上不需要做任何其他事情来避免链接器错误。这种方法的缺点是您将在每个翻译单元中拥有单独的 foo_helper()
副本,并且链接器无法合并这些副本。
您还可以做其他体操,主要涉及 sizeof...
,但我认为这些都不理想。
在下面的代码中,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
的定义移动到源文件中,因为它位于匿名命名空间中,因为它不应该是可访问的。
有办法解决这个问题吗?
inline void foo_helper() {};
解决了您的问题。
inline
主要是指 "conflicting definitions of this function are to be discarded, and one of the versions kept".
它还 non-bindingly 以模糊的方式建议 "inlining"(因为该标准并未真正涵盖内联是什么)。编译器可能会或可能不会注意该建议。
请注意,匿名命名空间不会 "make it unusable" 或其他任何东西。匿名命名空间旨在阻止 linker 冲突,仅此而已。创建一个名为 details
的命名空间,然后......好吧,相信用户不要去往里面戳。
在 header 中使用匿名命名空间是一个非常糟糕的主意。
如果另一个 header 文件中有一个 inline
函数(或模板函数)访问匿名命名空间中的符号或函数,您几乎肯定会有一个 ODR(一个定义规则)违规。这就是相同的 object、函数等有两个不同的定义,这是不允许的。
例如:
inline void bob() {
foo(1,2,3);
}
如果那是 #include
d 在两个不同的 .cpp 文件中,您刚刚制作了一个 ill-formed 程序(不需要诊断)。
这种格式错误的程序通常"behave the way you expect",但有时它们不会。例如,如果沿线某处你得到一个 static
局部变量,其存在取决于 ODR 违规,你可以让多个编译单元不同意哪个存在以及它的属性是什么。
从更一般的意义上讲,程序的 link 顺序可能会改变其行为,因为不同的定义是 "chosen"(可能存在极其细微的差异)。或者月相也可以做同样的事情。
ODR 违规出人意料地良性,直到它们用 non-local 难以追踪的错误来攻击你。
我将从旁白开始:在这里使用匿名名称空间不符合您的目的。由于您在头文件中定义它,因此它根本不受保护:它仍然在包含您的头文件的任何文件的范围内。此外,由于您已在匿名命名空间中定义它,因此每个使用它的翻译单元都会发出该函数的单独副本,并且链接器无法折叠它们。如果您真的希望它是私有的,那么这些天我并不是最好的 C++ 风格的升级者,所以也许其他人会纠正我,但我倾向于使用私有命名空间:
namespace my_stuff {
void foo_helper();
}
void foo() {
my_stuff::foo_helper();
}
正如 Yakk 所指出的,您可以使用内联函数,这将允许编译器将定义折叠为一个。在现代实践中,实际上不应该有另一个理由来避免使用 inline
关键字,因为现在编译器将自行决定是否内联函数,而不是听取您提供的提示。
由于您已经在匿名命名空间中定义了函数,正如我上面提到的,如果您保留它,您实际上不需要做任何其他事情来避免链接器错误。这种方法的缺点是您将在每个翻译单元中拥有单独的 foo_helper()
副本,并且链接器无法合并这些副本。
您还可以做其他体操,主要涉及 sizeof...
,但我认为这些都不理想。