Search code examples
c++linkage

Why can't I define a variable at global scope after declaring it in an unnamed namespace?


In my understanding:

I would expect the following to declare a variable and then define the same variable:

// Declare ::<unnamed>::my_obj with internal linkage and add my_obj to the
// enclosing namespace scope (the global namespace scope).
namespace { extern int my_obj; }

// Expected: Define the variable declared above.
// Actual: Declare and define a new object, ::my_obj.
int my_obj(42);

Instead, it declares two different objects, and warns me about the unused extern int my_obj.

Why doesn't the second my_obj define the first my_obj? Isn't it in scope?


Solution

  • The statement "names declared in an unnamed namespace are added to the enclosing namespace scope" does not mean that members of unnamed namespace become full-fledged members of enclosing namespace. This addition is performed by an implicit using-directive. Such addition makes these names visible to name lookup, but does not make them literal members of the enclosing namespace for other purposes.

    The problem you have in your code is the same as the one in the following snippet

    namespace A
    {
      void foo();
    }
    
    using namespace A;
    
    void foo() // <- Defines `::foo`, not `A::foo`
    {
    }
    
    // `A::foo` remains undefined
    

    Despite the fact that we explicitly "added" names from A to the global namespace, it still does not mean that we can define A::foo as a member of global namespace using unqualified name foo.

    In order to define A::foo in the above example you still have to refer to it using its qualified name A::foo. With unnamed namespaces this is impossible for obvious reasons.

    P.S. Compilers typically implement unnamed namespaces as namespaces with internal compiler-generated names. If you somehow figured out a "hack" to discover that name, technically you'd probably be able to define your my_obj separately by using a qualified name in the definition. But keep in mind that the hidden namespace name is different in different translation units, thus producing a unique my_obj variable in each translation unit.