10

I understand the use of unnamed namespaces to make functions and variables have internal linkage. Unnamed namespaces are not used in header files; only source files. Types declared in a source file cannot be used outside. So what's the use of putting types in unnamed namespaces?

See these links where it's mentioned that types can be put in unnamed namespaces:

Community
  • 1
  • 1
Francis Xavier
  • 205
  • 1
  • 10

3 Answers3

17

Where do you want to put local types other than the unnamed namespace? Types can't have a linkage specifier like static. If they are not publicly known, e.g., because they are declared in a header, there is a fair chance that names of local types conflict, e.g., when two translation units define types with the same name. In that case you'd end up with an ODR violation. Defining the types inside an unnamed namespace eliminates this possibility.

To be a bit more concrete. Consider you have

// file demo.h
int foo();
double bar();

// file foo.cpp
struct helper { int i; };
int foo() { helper h{}; return h.i; }

// file bar.cpp
struct helper { double d; }
double bar() { helper h{}; return h.d; }

// file main.cpp
#include "demo.h"
int main() {
     return foo() + bar();
}

If you link these three translation units, you have mismatching definitions of helper from foo.cpp and bar.cpp. The compiler/linker is not required to detect these but each type which is used in the program needs to have a consistent definition. Violating this constraints is known as violation of the "one definition rule" (ODR). Any violation of the ODR rule results in undefined behavior.

Given the comment it seems a bit more convincing is needed. The relevant section of the standard is 3.2 [basic.def.odr] paragraph 6:

There can be more than one definition of a class type (Clause 9), enumeration type (7.2), inline function with external linkage (7.1.2), class template (Clause 14), non-static function template (14.5.6), static data member of a class template (14.5.1.3), member function of a class template (14.5.1.1), or template specialization for which some template parameters are not specified (14.7, 14.5.5) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then each definition of D shall consist of the same sequence of tokens; and [...]

There are plenty of further constraints but "shall consist of the same sequence of tokens" is clearly sufficient to rule out e.g. the definitions in the demo above from being legal.

Dietmar Kühl
  • 141,209
  • 12
  • 196
  • 356
  • 2
    I just tried this: I put the same class declaration in the global namespace in multiple files and I didn't get any errors. It appears that the types are treated as local to the translation unit. So additionally putting them into unnamed namespaces seems to add no value? – Francis Xavier Aug 19 '15 at 18:27
  • 4
    @FrancisXavier Try using a non-trivial default constructor: `struct helper { helper() { std::cout << __FILE__ << "\n"; } };` Define two `helper` in different files, construct an instance of each one from a function in the corresponding file, call both functions from `main`. Compile **without** optimizations. There will be two symbols for `helper::helper()`, and since it's (implicitly) inline, one will be thrown away. The remaining one will be called from both functions (gcc 4.9.2 on linux x64, this behaviour is not guaranteed). – dyp Aug 19 '15 at 18:39
  • Yes, of course. I've appended a slight hint at UB, but the main reason for my comment was of course to show an example where something unexpected happens. -- Other bad things happen e.g. when the type is used as a parameter or return type of a function (even indirectly as a data member of a type used that way). -- Edit: d'oh, I should have scrolled down further and read R Sahu's answer before commenting. – dyp Aug 19 '15 at 18:45
  • @dyp Reg. your first comment. in that particular case he is not actually violating ODR, is he? given that the `helper` definition would be word-for-word identical? – ForeverLearning Aug 20 '15 at 16:20
  • Ah! I completely missed the `__FILE__` directive! I get it now. Also I was using loose terminology. I did mean tokens. – ForeverLearning Aug 20 '15 at 17:00
4

So what's the use of putting types in unnamed namespaces?

You can create short, meaningful classes with names that maybe used in more than one file without the problem of name conflicts.

For example, I use two classes often in unnamed namespaces - Initializer and Helper.

namespace
{
   struct Initializer
   {
      Initializer()
      {
         // Take care of things that need to be initialized at static
         // initialization time.
      }
   };

   struct Helper
   {
      // Provide functions that are useful for the implementation
      // but not exposed to the users of the main interface.
   };

   // Take care of things that need to be initialized at static
   // initialization time.
   Initializer initializer;
}

I can repeat this pattern of code in as many files as I want without the names Initializer and Helper getting in the way.

Update, in response to comment by OP

file-1.cpp:

struct Initializer
{
   Initializer();
};

Initializer::Initializer()
{
}

int main()
{
   Initializer init;
}

file-2.cpp:

struct Initializer
{
   Initializer();
};

Initializer::Initializer()
{
}

Command to build:

g++ file-1.cpp file-2.cpp

I get linker error message about multiple definitions of Initializer::Initializer(). Please note that the standard does not require the linker to produce this error. From section 3.2/4:

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required.

The linker does not produce an error if the functions are defined inline:

struct Initializer
{
   Initializer() {}
};

That's OK for a simple case like this since the implementations are identical. If the inline implementations are different, the program is subject to undefined behavior.

R Sahu
  • 196,807
  • 13
  • 136
  • 247
  • 1
    My point is, the types Initializer and Helper don't need to be in an unnamed namespace. Only the initializer variable does. If you declared the types outside the unnamed namespace, your code would work exactly the same. So why put them inside? – Francis Xavier Aug 19 '15 at 18:18
  • 1
    If multiple files have `Initializer` and `Helper` outside the unnamed namespace, there will be conflicts at link time. – R Sahu Aug 19 '15 at 18:20
  • No, even if multiple cpp files have Initializer and Helper outside the unnamed namespace, there will not be any conflicts! Try it. Types are local to the cpp file and are not visible outside. – Francis Xavier Aug 19 '15 at 18:22
  • 3
    @FrancisXavier Not everything which works (or appears to work) is necessarily correct. Two different definitions of the same type are a violation of the One Definition Rule. Such a program has Undefined Behaviour and anything can happen. For example, if a call to a member function of such a type was not inlined, the linker could silently drop one of the conflicting definitions and redirect all calls to one body. – Angew is no longer proud of SO Aug 19 '15 at 18:28
  • @RSahu: your linker error was not related to the struct Initializer being defined multiple times. It was because the function Initializer::Initializer() was being defined multiple times. My question was specifically about types, not functions or variables. – Francis Xavier Aug 19 '15 at 18:50
  • 1
    @FrancisXavier You can't put the constructor definition in an unnamed namespace without also having the type declaration in the same unnamed namespace. – Chris Drew Aug 19 '15 at 19:02
  • @ChrisDrew: Agreed. But my question was not about this. My question was about why the type should be put into an unnamed namespace. Whether the type has functions which are then defined outside of the type is unrelated. – Francis Xavier Aug 19 '15 at 19:15
  • @FrancisXavier What do you mean? Functions are not defined either inside or outside a type. Member functions can be inlined in a class definition or not. – curiousguy Aug 19 '15 at 20:35
  • @curiousguy: By inside/outside I meant inlined/not inlined. In the code posted above, if the 'inline' keyword were used, there would not be any linker error: inline Initializer::Initializer() { }. But this is not related to my question. – Francis Xavier Aug 19 '15 at 20:46
1

I might be a bit late for answering the question the OP made but since I think the answer is not fully clear, I would like to help future readers.

Lets try a test... compile the following files:

//main.cpp
#include <iostream>
#include "test.hpp"

class Test {
public:
     void talk() {
      std::cout<<"I'm test MAIN\n";
     }
};

int main()
{
     Test t;
     t.talk();
     testfunc();    
}

//test.hpp
void testfunc();

//test.cpp
#include <iostream>

class Test {
public:
     void talk()
      {
           std::cout<<"I'm test 2\n";
      }
};


void testfunc() {
     Test t;
     t.talk();
}

Now run the executable. You would expect to see:

I'm test MAIN
I'm test 2

What you should see thought is:

I'm test MAIN
I'm test MAIN

What happened?!?!!

Now try putting an unnamed namespace around the "Test" class in "test.cpp" like so:

#include <iostream>
#include "test.hpp"

namespace{
     class Test {
     public:
      void talk()
           {
            std::cout<<"I'm test 2\n";
           }
     };
}

void testfunc() {
     Test t;
     t.talk();
}

Compile it again and run. The output should be:

I'm test MAIN
I'm test 2

Wow! It works!


As it turns out, it is important to define classes inside unnamed namespaces so that you get the proper functionality out of them when two class names in different translation units are identical. Now as to why that is the case, I haven't done any research on it (maybe someone could help here?) and so I can't really tell you for sure. I'm answering purely from a practical standpoint.

What I would suspect though is that, while it is true that C structs are indeed local to a translation unit, they are a bit different from classes since classes in c++ usually have behavior assigned to them. Behavior means functions and as we know, functions are not local to the translation unit.

This is just my assumption.

  • this is an interesting observation, I too would be interested as to why this is the case. I guess it makes sense that if an implementation for the function `::Test::talk` was already found in earlier translation units, other implementations are ignored, but in what way does explicitly declaring the unnamed namespace change this? And shouldn't cause a warning at the least? – gordonk Dec 27 '17 at 14:13