C++ 未命名/匿名命名空间与静态函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/154469/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Unnamed/anonymous namespaces vs. static functions
提问by Head Geek
A feature of C++ is the ability to create unnamed (anonymous) namespaces, like so:
C++ 的一个特性是能够创建未命名(匿名)命名空间,如下所示:
namespace {
int cannotAccessOutsideThisFile() { ... }
} // namespace
You would think that such a feature would be useless -- since you can't specify the name of the namespace, it's impossible to access anything within it from outside. But these unnamed namespaces areaccessible within the file they're created in, as if you had an implicit using-clause to them.
您会认为这样的功能没有用——因为您无法指定名称空间的名称,因此不可能从外部访问其中的任何内容。但是这些未命名的命名空间可以在创建它们的文件中访问,就好像您对它们有一个隐式的 using 子句一样。
My question is, why or when would this be preferable to using static functions? Or are they essentially two ways of doing the exact same thing?
我的问题是,为什么或何时这比使用静态函数更可取?或者它们本质上是做完全相同的事情的两种方式?
采纳答案by luke
The C++ Standard reads in section 7.3.1.1 Unnamed namespaces, paragraph 2:
C++ 标准在第 7.3.1.1 节未命名命名空间,第 2 段中读到:
The use of the static keyword is deprecated when declaring objects in a namespace scope, the unnamed-namespace provides a superior alternative.
在命名空间范围内声明对象时不推荐使用 static 关键字,未命名命名空间提供了一个更好的选择。
Static only applies to names of objects, functions, and anonymous unions, not to type declarations.
静态仅适用于对象、函数和匿名联合的名称,不适用于类型声明。
Edit:
编辑:
The decision to deprecate this use of the static keyword (affect visibility of a variable declaration in a translation unit) has been reversed (ref). In this case using a static or an unnamed namespace are back to being essentially two ways of doing the exact same thing. For more discussion please see thisSO question.
不赞成使用 static 关键字(影响翻译单元中变量声明的可见性)的决定已被撤销(ref)。在这种情况下,使用静态或未命名的命名空间本质上又回到了做完全相同事情的两种方式。有关更多讨论,请参阅此SO 问题。
Unnamed namespaces still have the advantage of allowing you to define translation-unit-local types. Please see thisSO question for more details.
未命名的命名空间仍然具有允许您定义翻译单元本地类型的优势。有关更多详细信息,请参阅此SO 问题。
Credit goes to Mike Percyfor bringing this to my attention.
幸得迈克·珀西提出这个引起我的注意。
回答by hazzen
Putting methods in an anonymous namespace prevents you from accidentally violating the One Definition Rule, allowing you to never worry about naming your helper methods the same as some other method you may link in.
将方法放在匿名命名空间中可以防止您意外违反单一定义规则,让您永远不必担心将您的助手方法命名为与您可能链接的其他方法相同的方法。
And, as pointed out by luke, anonymous namespaces are preferred by the standard over static members.
而且,正如 luke 所指出的,与静态成员相比,标准更喜欢匿名命名空间。
回答by Richard Corden
There is one edge case where static has a surprising effect(at least it was to me). The C++03 Standard states in 14.6.4.2/1:
在一种极端情况下,静态具有令人惊讶的效果(至少对我而言是这样)。C++03 标准在 14.6.4.2/1 中声明:
For a function call that depends on a template parameter, if the function name is an unqualified-idbut not a template-id, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2) except that:
- For the part of the lookup using unqualified name lookup (3.4.1), only function declarations with external linkage from the template definition context are found.
- For the part of the lookup using associated namespaces (3.4.2), only function declarations with external linkage found in either the template definition context or the template instantiation context are found.
...
对于依赖于模板参数的函数调用,如果函数名称是unqualified-id但不是template-id,则使用通常的查找规则 (3.4.1, 3.4.2) 找到候选函数,除了:
- 对于使用非限定名称查找 (3.4.1) 的查找部分,只能找到来自模板定义上下文的具有外部链接的函数声明。
- 对于使用关联命名空间(3.4.2)的查找部分,只找到在模板定义上下文或模板实例化上下文中找到的具有外部链接的函数声明。
...
The below code will call foo(void*)
and not foo(S const &)
as you might expect.
下面的代码将调用,foo(void*)
而不是foo(S const &)
您所期望的。
template <typename T>
int b1 (T const & t)
{
foo(t);
}
namespace NS
{
namespace
{
struct S
{
public:
operator void * () const;
};
void foo (void*);
static void foo (S const &); // Not considered 14.6.4.2(b1)
}
}
void b2()
{
NS::S s;
b1 (s);
}
In itself this is probably not that big a deal, but it does highlight that for a fully compliant C++ compiler (i.e. one with support for export
) the static
keyword will still have functionality that is not available in any other way.
就其本身而言,这可能不是什么大问题,但它确实强调了对于完全兼容的 C++ 编译器(即支持 的编译器export
),static
关键字仍然具有以任何其他方式不可用的功能。
// bar.h
export template <typename T>
int b1 (T const & t);
// bar.cc
#include "bar.h"
template <typename T>
int b1 (T const & t)
{
foo(t);
}
// foo.cc
#include "bar.h"
namespace NS
{
namespace
{
struct S
{
};
void foo (S const & s); // Will be found by different TU 'bar.cc'
}
}
void b2()
{
NS::S s;
b1 (s);
}
The only way to ensure that the function in our unnamed namespace will not be found in templates using ADL is to make it static
.
确保在使用 ADL 的模板中找不到我们未命名命名空间中的函数的唯一方法是 make it static
。
Update for Modern C++
现代 C++ 的更新
As of C++ '11, members of an unnamed namespace have internal linkage implicitly (3.5/4):
从 C++ '11 开始,未命名命名空间的成员隐式具有内部链接 (3.5/4):
An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage.
未命名命名空间或在未命名命名空间内直接或间接声明的命名空间具有内部链接。
But at the same time, 14.6.4.2/1 was updated to remove mention of linkage (this taken from C++ '14):
但同时,更新了 14.6.4.2/1 以删除链接的提及(这取自 C++ '14):
For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2) except that:
For the part of the lookup using unqualified name lookup (3.4.1), only function declarations from the template definition context are found.
For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context or the template instantiation context are found.
对于后缀表达式是依赖名称的函数调用,使用通常的查找规则 (3.4.1, 3.4.2) 找到候选函数,除了:
对于使用非限定名称查找 (3.4.1) 的查找部分,只能找到来自模板定义上下文的函数声明。
对于使用关联命名空间(3.4.2)的查找部分,只找到在模板定义上下文或模板实例化上下文中找到的函数声明。
The result is that this particular difference between static and unnamed namespace members no longer exists.
结果是静态和未命名命名空间成员之间的这种特殊差异不再存在。
回答by Richard Corden
I recently began replacing static keywords with anonymous namespaces in my code but immediately ran into a problem where the variables in the namespace were no longer available for inspection in my debugger. I was using VC60, so I don't know if that is a non-issue with other debuggers. My workaround was to define a 'module' namespace, where I gave it the name of my cpp file.
我最近开始在我的代码中用匿名命名空间替换静态关键字,但立即遇到了一个问题,即命名空间中的变量在我的调试器中不再可用。我使用的是 VC60,所以我不知道这是否与其他调试器无关。我的解决方法是定义一个“模块”命名空间,在那里我给了它我的 cpp 文件的名称。
For example, in my XmlUtil.cpp file, I define a namespace XmlUtil_I { ... }
for all of my module variables and functions. That way I can apply the XmlUtil_I::
qualification in the debugger to access the variables. In this case, the _I
distinguishes it from a public namespace such as XmlUtil
that I may want to use elsewhere.
例如,在我的 XmlUtil.cpp 文件中,我XmlUtil_I { ... }
为我的所有模块变量和函数定义了一个命名空间。这样我就可以XmlUtil_I::
在调试器中应用限定来访问变量。在这种情况下,_I
将它与公共名称空间区分开来,例如XmlUtil
我可能想在其他地方使用的名称空间。
I suppose a potential disadvantage of this approach compared to a truly anonymous one is that someone could violate the desired static scope by using the namespace qualifier in other modules. I don't know if that is a major concern though.
我认为与真正匿名的方法相比,这种方法的潜在缺点是有人可能会通过在其他模块中使用命名空间限定符来违反所需的静态范围。我不知道这是否是一个主要问题。
回答by Firas Assaad
Use of static keyword for that purpose is deprecated by the C++98 standard. The problem with static is that it doesn't apply to type definition. It's also an overloaded keyword used in different ways in different contexts, so unnamed namespaces simplify things a bit.
C++98 标准不推荐为此目的使用 static 关键字。静态的问题在于它不适用于类型定义。它也是在不同上下文中以不同方式使用的重载关键字,因此未命名的命名空间可以简化一些事情。
回答by Don Wakefield
From experience I'll just note that while it is the C++ way to put formerly-static functions into the anonymous namespace, older compilers can sometimes have problems with this. I currently work with a few compilers for our target platforms, and the more modern Linux compiler is fine with placing functions into the anonymous namespace.
根据经验,我只是注意到,虽然将以前的静态函数放入匿名命名空间是 C++ 的方式,但较旧的编译器有时会遇到此问题。我目前为我们的目标平台使用一些编译器,更现代的 Linux 编译器可以将函数放入匿名命名空间。
But an older compiler running on Solaris, which we are wed to until an unspecified future release, will sometimes accept it, and other times flag it as an error. The error is not what worries me, it's what it mightbe doing when it acceptsit. So until we go modern across the board, we are still using static (usually class-scoped) functions where we'd prefer the anonymous namespace.
但是在 Solaris 上运行的较旧的编译器,在未指定的未来版本之前,我们一直坚持使用它,有时会接受它,有时会将其标记为错误。错误不是我担心的,而是它接受它时可能会做的事情。因此,在我们全面实现现代化之前,我们仍然使用静态(通常是类作用域)函数,而我们更喜欢匿名命名空间。
回答by Chris
In addition if one uses static keyword on a variable like this example:
此外,如果像这个例子一样在变量上使用 static 关键字:
namespace {
static int flag;
}
It would not be seen in the mapping file
它不会在映射文件中看到
回答by masrtis
A compiler specific difference between anonymous namespaces and static functions can be seen compiling the following code.
匿名命名空间和静态函数之间的编译器特定差异可以在编译以下代码时看到。
#include <iostream>
namespace
{
void unreferenced()
{
std::cout << "Unreferenced";
}
void referenced()
{
std::cout << "Referenced";
}
}
static void static_unreferenced()
{
std::cout << "Unreferenced";
}
static void static_referenced()
{
std::cout << "Referenced";
}
int main()
{
referenced();
static_referenced();
return 0;
}
Compiling this code with VS 2017 (specifying the level 4 warning flag /W4 to enable warning C4505: unreferenced local function has been removed) and gcc 4.9 with the -Wunused-function or -Wall flag shows that VS 2017 will only produce a warning for the unused static function. gcc 4.9 and higher, as well as clang 3.3 and higher, will produce warnings for the unreferenced function in the namespace and also a warning for the unused static function.
使用 VS 2017 编译此代码(指定级别 4 警告标志 /W4 以启用警告 C4505:未引用的本地函数已被删除)和带有 -Wunused-function 或 -Wall 标志的 gcc 4.9 显示 VS 2017 只会为未使用的静态函数。gcc 4.9 及更高版本,以及 clang 3.3 及更高版本,将对命名空间中未引用的函数产生警告,并对未使用的静态函数产生警告。
回答by Pavel P
Personally I prefer static functions over nameless namespaces for the following reasons:
我个人更喜欢静态函数而不是无名命名空间,原因如下:
it's obvious and clear from function definition alone that it's private to the translation unit where it's compiled. With namesless namespace you might need to scroll and search to see if a function is in a namespace.
functions in namespaces might be treated as extern by some (older) compilers. In VS2017 they are still extern. For this reason even if a function is in nameless namespace you might still want to mark them static.
static functions behave very similar in C or C++, while nameless namespaces are obviously C++ only. nameless namespaces also add extra level if indentation and I don't like that :)
仅从函数定义就可以清楚地看出,它对编译它的翻译单元是私有的。对于无名命名空间,您可能需要滚动和搜索以查看函数是否在命名空间中。
命名空间中的函数可能会被某些(较旧的)编译器视为 extern。在 VS2017 中,它们仍然是外部的。出于这个原因,即使函数在无名命名空间中,您可能仍然希望将它们标记为静态。
静态函数在 C 或 C++ 中的行为非常相似,而无名命名空间显然只是 C++。如果缩进,无名命名空间也会增加额外的级别,我不喜欢这样:)
So, I'm happy to see that use of static for functions isn't deprecated anymore.
所以,我很高兴看到不再不推荐使用静态函数。
回答by Commodore Jaeger
Having learned of this feature only just now while reading your question, I can only speculate. This seems to provide several advantages over a file-level static variable:
刚刚在阅读您的问题时才了解此功能,我只能推测。这似乎比文件级静态变量提供了几个优点:
- Anonymous namespaces can be nested within one another, providing multiple levels of protection from which symbols can not escape.
- Several anonymous namespaces could be placed in the same source file, creating in effect different static-level scopes within the same file.
- 匿名命名空间可以相互嵌套,提供多个级别的保护,符号无法逃脱。
- 多个匿名命名空间可以放置在同一个源文件中,从而在同一个文件中创建不同的静态级别作用域。
I'd be interested in learning if anyone has used anonymous namespaces in real code.
我有兴趣了解是否有人在实际代码中使用过匿名命名空间。