C++ 文件作用域静态函数
C++ file-scope static functions
什么时候应该考虑使用在文件范围内定义的静态函数?
当在一个这样的函数中完成的任务实际上不属于任何 class 的成员函数,并且只在某个源文件中(重复)需要这样的任务时,我通常会使用它们。
我的用法是否符合此功能存在的原因?还是我劫持了一个用于其他用途的概念?
这是对文件范围静态函数的一种完全有效的使用,但请记住,静态的这种用法已经被弃用了很长一段时间。通常首选使用匿名命名空间。
当函数 and/of 数据不是 class 接口的一部分而是实现细节的一部分有意义时,我会做类似的事情。
但我不使用关键字 static。相反,我将函数 and/or 数据放在未命名的命名空间中。
首先,您要查找的术语是 internal linkage。您的问题确实应该是:"Which entities should have internal linkage?" static
关键字,或者未命名的名称空间,只是实现内部 linkage.
的实现机制
现在答案应该很明显了:所有只在一个翻译单元内需要的实体都可以被赋予内部 linkage,这样做有好处:一个是编译器可以利用实体无法被任何其他翻译单元看到的信息,因此它可以例如避免发出可能需要的代码,或者更积极地内联。另一个原因是,如果您碰巧选择了在其他一些 TU 中也在本地使用的名称,则可以最大限度地降低意外违反 ODR 的风险。
一个典型的例子是这样的:
my_lib.hpp:
#ifndef H_MY_LIB
#define H_MY_LIB
namespace foo
{
void do_an_awesome_thing(void * p, std::size_t n);
}
#endif
my_lib.cpp:
#include "my_lib.hpp"
namespace foo
{
namespace
{
void helper(void * p) { /* ... */ }
bool aux(std::size_t n, float f) { /* ... */ }
constexpr char usage[] = R"(This is how you do it...)";
constexpr float some_factor = 1.25;
}
void do_an_awesome_thing(void *p, std::size_t n)
{
if (!aux(n, some_factor)) { LOG_AND_DIE(usage); }
helper(p);
}
}
现在您可以确定您的翻译单元不会对程序的其余部分施加任何不当的 link 时间负担。
未命名命名空间的放置是个人喜好问题;您可以将它放在您常用的名称空间中,也可以放在顶层。效果是一样的。
什么时候应该考虑使用在文件范围内定义的静态函数?
当在一个这样的函数中完成的任务实际上不属于任何 class 的成员函数,并且只在某个源文件中(重复)需要这样的任务时,我通常会使用它们。
我的用法是否符合此功能存在的原因?还是我劫持了一个用于其他用途的概念?
这是对文件范围静态函数的一种完全有效的使用,但请记住,静态的这种用法已经被弃用了很长一段时间。通常首选使用匿名命名空间。
当函数 and/of 数据不是 class 接口的一部分而是实现细节的一部分有意义时,我会做类似的事情。
但我不使用关键字 static。相反,我将函数 and/or 数据放在未命名的命名空间中。
首先,您要查找的术语是 internal linkage。您的问题确实应该是:"Which entities should have internal linkage?" static
关键字,或者未命名的名称空间,只是实现内部 linkage.
现在答案应该很明显了:所有只在一个翻译单元内需要的实体都可以被赋予内部 linkage,这样做有好处:一个是编译器可以利用实体无法被任何其他翻译单元看到的信息,因此它可以例如避免发出可能需要的代码,或者更积极地内联。另一个原因是,如果您碰巧选择了在其他一些 TU 中也在本地使用的名称,则可以最大限度地降低意外违反 ODR 的风险。
一个典型的例子是这样的:
my_lib.hpp:
#ifndef H_MY_LIB
#define H_MY_LIB
namespace foo
{
void do_an_awesome_thing(void * p, std::size_t n);
}
#endif
my_lib.cpp:
#include "my_lib.hpp"
namespace foo
{
namespace
{
void helper(void * p) { /* ... */ }
bool aux(std::size_t n, float f) { /* ... */ }
constexpr char usage[] = R"(This is how you do it...)";
constexpr float some_factor = 1.25;
}
void do_an_awesome_thing(void *p, std::size_t n)
{
if (!aux(n, some_factor)) { LOG_AND_DIE(usage); }
helper(p);
}
}
现在您可以确定您的翻译单元不会对程序的其余部分施加任何不当的 link 时间负担。
未命名命名空间的放置是个人喜好问题;您可以将它放在您常用的名称空间中,也可以放在顶层。效果是一样的。