extern int foo(); // declaration extern int bar = 3; // definition int baz() // definition, which is extern by default { return 4; }
So what is the exception to the rule? Anything declared or defined in an anonymous namespace (aka unnamed namespace) has "internal linkage", like a namespace-level static variable. In other words, it cannot be accessed outside the current translation unit, as mentioned here. You can, however, still use extern in an anonymous namespace, and there are actually useful things you can do with it!
Breaking Circular Dependencies
Let's say you need to declare a global variable before using it, but you want to keep the symbols private to the file to avoid name-collisions. Let's try to use the 'static' keyword:struct Funcs { int(*fn)(void); }; static Funcs global_var; int UseGlobalVar() { return global_var.fn(); } static Funcs global_var = { &UseGlobalVar };This might have been fine in C, but it doesn't compile in C++ -- we get error : redefinition of 'global_var' from the compiler, because there's no way to forward-declare a static variable in C++. We could change 'static' to 'extern', but then it wouldn't be file-private. The solution? Anonymous namespaces.
namespace { extern Funcs global_var; // fwd-decl with extern, but has internal linkage } int UseGlobalVar() { return global_var.fn(); } namespace { Funcs global_var = { &UseGlobalVar }; }Even though global_var is marked extern, the anonymous namespace has precedence and gives it internal linkage, and everything works out.
Non-Type Template Arguments
You're probably familiar with non-type template parameters. For example, in std::arrayYou probably also know that object-pointers/references and function-pointers/references are valid non-type template parameters. But did you know that in C++03, any argument you plug into those must have external linkage?! Even worse, many compilers didn't get the memo that this rule was relaxed in C++11. As a contrived example, let's say we want to pre-bake a constant double by reference:
template <const double& Addend> double add(double x) { return x + Addend; } double one = 1.0; static double two = 2.0; const double three = 3.0; namespace { double four = 4.0; const double five = 5.0; extern double six = 6.0; } int main() { printf("%f\n", add<one>(0)); #1 // ok printf("%f\n", add<two>(0)); #2 // error printf("%f\n", add<three>(0)); #3 // error printf("%f\n", add<four>(0)); #4 // ok printf("%f\n", add<five>(0)); #5 // error printf("%f\n", add<six>(0)); #6 // okFrom MSVC 2015 we get errors like:
program.cpp(36): error C2970: 'add': template parameter 'Addend': 'two': an expression involving objects with internal linkage cannot be used as a non-type argumentLet's go through each case:
- namespace-level objects have external linkage by default ==> ok
- static at namespace level implies internal linkage ==> error
- const at namespace level implies internal linkage ==> error
- surprise! same as #1
- same as #3
- surprise! same as #1
No comments:
Post a Comment