Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Unnamed namespaces are used to define a namespace that is unique to the translation unit, where the names contained within have internal linkage by default. The C++ Standard, [namespace.unnamed], paragraph 1 [ISO/IEC 14882-2014], states the following:

An unnamed-namespace-definition behaves as if it were replaced by:

  inline namespace unique { /* empty body */ }
using namespace unique ;
namespace unique { namespace-body }

where inline appears if and only if it appears in the unnamed-namespace-definition, all occurrences of unique in a translation unit are replaced by the same identifier, and this identifier differs from all other identifiers in the entire program.

...

...

ODR-use should be further clarified.

Production-quality C++ code frequently uses header files as a means to share code between translation units. A header file is any file that is inserted into a translation unit through an #include directive. Do not define an unnamed namespace in a header file. When an unnamed namespace is defined in a header file, it can lead to surprising results. Due to default internal linkage, each translation unit will define its own unique instance of members of the unnamed namespace that are ODR-used within that translation unit. This can cause unexpected results, bloat the resulting executable, or inadvertently trigger undefined behavior due to one-definition rule (ODR) violations.

Page properties
hiddentrue

This rule could be argued to be more of a style recommendation than a rule. However, this is prevalent advice given by many sources, such as Google's coding style guidelines (http://google-styleguide.googlecode.com/svn/trunk/cppguide.html#Namespaces), and the behavior is subtle-but-surprising enough that it will trip up programmers. The most common rationale behind why unnamed namespaces are bad in a header file has to do with ODR violations, but from what I can research, the unnamed namespace in a header isn't to blame for those violations per-se, it's misunderstanding what an unnamed namespace does in conjunction with some other language feature, like inline functions.

I am writing this to be a rule, but we'll see what kind of push-back happens in practice as to whether it remains a rule, or gets demoted to a strong recommendation.

Noncompliant Code Example

In this noncompliant code example, the variable v is defined in an unnamed namespace within a header file and is accessed from two separate translation units. Each translation unit prints the current value of v and then assigns a new value into it. However, because v is defined within an unnamed namespace, each translation unit operates on its own instance of v, resulting in unexpected output.

Code Block
bgColor#FFcccc
langcpp
// a.h
#ifndef A_HEADER_FILE
#define A_HEADER_FILE
 
namespace {
int v;
}
 
#endif // A_HEADER_FILE
 
// a.cpp
#include "a.h"
#include <iostream>
 
void f() {
  std::cout << "f(): " << v << std::endl;
  v = 42;
  // ...
}
 
// b.cpp
#include "a.h"
#include <iostream>
 
void g() {
  std::cout << "g(): " << v << std::endl;
  v = 100;
}
 
int main() {
  extern void f();
  f(); // Prints v, sets it to 42
  g(); // Prints v, sets it to 100
  f();
  g();
}

When executed, this program prints the following.

Code Block
f(): 0
g(): 0
f(): 42
g(): 100

Compliant Solution

In this compliant solution, v is defined in only one translation unit but is externally visible to all translation units, resulting in the expected behavior.

Code Block
bgColor#ccccff
langcpp
// a.h
#ifndef A_HEADER_FILE
#define A_HEADER_FILE
 
extern int v;
 
#endif // A_HEADER_FILE
 
// a.cpp
#include "a.h"
#include <iostream>

int v; // Definition of global variable v
 
void f() {
  std::cout << "f(): " << v << std::endl;
  v = 42;
  // ...
}
 
// b.cpp
#include "a.h"
#include <iostream>
 
void g() {
  std::cout << "g(): " << v << std::endl;
  v = 100;
}
 
int main() {
  extern void f();
  f(); // Prints v, sets it to 42
  g(); // Prints v, sets it to 100
  f(); // Prints v, sets it back to 42
  g(); // Prints v, sets it back to 100
}

When executed, this program prints the following.

Code Block
f(): 0
g(): 42
f(): 100
g(): 42

Noncompliant Code Example

In this noncompliant code example, the variable v is defined in an unnamed namespace within a header file, and an inline function, get_v(), is defined, which accesses that variable. ODR-using the inline function from multiple translation units (as shown in the implementation of f() and g()) violates the one-definition rule because the definition of get_v() is not identical in all translation units ( due to referencing a unique v in each translation unit).

Code Block
bgColor#FFcccc
langcpp
// a.h
#ifndef A_HEADER_FILE
#define A_HEADER_FILE
 
namespace {
int v;
}
 
inline int get_v() { return v; }
 
#endif // A_HEADER_FILE
 
// a.cpp
#include "a.h"
 
void f() {
  int i = get_v();
  // ...
}
 
// b.cpp
#include "a.h"
 
void g() {
  int i = get_v();
  // ...
}

See MSC52DCL60-CPP. Obey the one-definition rule for more information on violations of the one-definition rule.

Compliant Solution

In this compliant solution, v is defined in only one translation unit but is externally visible to all translation units and can be accessed from the inline get_v() function:.

Code Block
bgColor#ccccff
langcpp
// a.h
#ifndef A_HEADER_FILE
#define A_HEADER_FILE
 
extern int v;

inline int get_v() {
  return v;
}

#endif // A_HEADER_FILE
 
// a.cpp
#include "a.h"
 
// Externally used by get_v();
int v;
 
void f() {
  int i = get_v();
  // ...
}
 
// b.cpp
#include "a.h"
 
void g() {
  int i = get_v();
  // ...
}

Noncompliant Code Example

In this noncompliant code example, the function f() is defined within a header file. However, including the header file in multiple translation units causes a violation of the one-definition rule that usually results in an error diagnostic generated at link time due to multiple definitions of a function with the same name.

Code Block
bgColor#FFcccc
langcpp
// a.h
#ifndef A_HEADER_FILE
#define A_HEADER_FILE
 
void f() { /* ... */ }
 
#endif // A_HEADER_FILE
 
// a.cpp
#include "a.h"
// ...
 
// b.cpp
#include "a.h"
// ...

Noncompliant Code Example

This noncompliant code example attempts to resolve the link-time errors by defining f() within an unnamed namespace. However, it produces multiple, unique definitions of f() in the resulting executable. If a.h is included from many translation units, it can lead to increased link times, a larger executable file, and reduced performance.

Code Block
bgColor#FFcccc
langcpp
// a.h
#ifndef A_HEADER_FILE
#define A_HEADER_FILE
 
namespace { 
void f() { /* ... */ }
}
 
#endif // A_HEADER_FILE
 
// a.cpp
#include "a.h"
// ...
 
// b.cpp
#include "a.h"
// ...

Compliant Solution

In this compliant solution, f() is not defined with an unnamed namespace and is instead defined as an inline function. Inline functions are required to be defined identically in all the translation units in which they are used, which allows an implementation to generate only a single instance of the function at runtime in the event the body of the function does not get generated for each call site.

Code Block
bgColor#ccccff
langcpp
// a.h
#ifndef A_HEADER_FILE
#define A_HEADER_FILE
 
inline void f() { /* ... */ }
 
#endif // A_HEADER_FILE
 
// a.cpp
#include "a.h"
// ...
 
// b.cpp
#include "a.h"
// ...

...

In this noncompliant code example, the variable v is defined in an unnamed namespace within a header file and is accessed from two separate translation units. Each translation unit prints the current value of v and then assigns a new value into it. However, because v is defined within an unnamed namespace, each translation unit operates on its own instance of v, resulting in unexpected output.

Code Block
bgColor#FFcccc
langcpp
// a.h
#ifndef A_HEADER_FILE
#define A_HEADER_FILE
 
namespace {
int v;
}
 
#endif // A_HEADER_FILE
 
// a.cpp
#include "a.h"
#include <iostream>
 
void f() {
  std::cout << "f(): " << v << std::endl;
  v = 42;
  // ...
}
 
// b.cpp
#include "a.h"
#include <iostream>
 
void g() {
  std::cout << "g(): " << v << std::endl;
  v = 100;
}
 
int main() {
  extern void f();
  f(); // Prints v, sets it to 42
  g(); // Prints v, sets it to 100
  f();
  g();
}

When executed, this program prints the following:

Code Block
f(): 0
g(): 0
f(): 42
g(): 100

Compliant Solution

In this compliant solution, v is defined in only one translation unit but is externally visible to all translation units, resulting in the expected behavior:

Code Block
bgColor#ccccff
langcpp
// a.h
#ifndef A_HEADER_FILE
#define A_HEADER_FILE
 
extern int v;
 
#endif // A_HEADER_FILE
 
// a.cpp
#include "a.h"
#include <iostream>

int v; // Definition of global variable v
 
void f() {
  std::cout << "f(): " << v << std::endl;
  v = 42;
  // ...
}
 
// b.cpp
#include "a.h"
#include <iostream>
 
void g() {
  std::cout << "g(): " << v << std::endl;
  v = 100;
}
 
int main() {
  extern void f();
  f(); // Prints v, sets it to 42
  g(); // Prints v, sets it to 100
  f(); // Prints v, sets it back to 42
  g(); // Prints v, sets it back to 100
}

When executed, this program prints the following:

Code Block
f(): 0
g(): 42
f(): 100
g(): 42
Page properties
hiddentrue

I suspect that we may want an exception for allowing unnamed namespaces in header files under certain circumstances. For instance, it may be permissible for constant values in unnamed namespaces (but then again, those could still result in ODR violations). I know that some implementations of bind() (such as Boost's) put the placeholders into an unnamed namespace, but smart folks like Dave Abrahams have said this is likely a mistake and a bug. It's worth doing further research as to what exceptions, if any, would be useful.

Risk Assessment

Defining an unnamed namespace within a header file can cause data integrity violations and performance problems but is unlikely to go unnoticed with sufficient testing. One-definition rule violations result in undefined behavior.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

DCL58

DCL59-CPP

Medium

Unlikely

Medium

P4

L3

Automated Detection 

Tool

Version

Checker

Description

Astrée

Include Page
Astrée_V
Astrée_V

unnamed-namespace-header
Fully checked
Axivion Bauhaus Suite

Include Page
Axivion Bauhaus Suite_V
Axivion Bauhaus Suite_V

CertC++-DCL59
Clang
Include Page
Clang_V
Clang_V
 google
cert-
build
dcl59-
namespaces
cpp
Checked by clang-tidy
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

LANG.STRUCT.DECL.ANH

Anonymous Namespace in Header File

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C++2518
Klocwork
Include Page
Klocwork_V
Klocwork_V

MISRA.NAMESPACE.UNMD


LDRA tool suite
Include Page
LDRA_V
LDRA_V

286 S, 512 S

Fully implemented

Parasoft C/C++test
9.5CODSTA-74 
Include Page
Parasoft_V
Parasoft_V
CERT_CPP-DCL59-a

There shall be no unnamed namespaces in header files

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C++: DCL59-CPPChecks for unnamed namespaces in header files (rule fully covered)
RuleChecker
Include Page
RuleChecker_V
RuleChecker_V
unnamed-namespace-header
Fully checked
SonarQube C/C++ Plugin
Include Page
SonarQube C/C++ Plugin_V
SonarQube C/C++ Plugin_V
UnnamedNamespaceInHeader

PVS-Studio

Include Page
PVS-Studio_V
PVS-Studio_V

V1068
 

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

Bibliography

[ISO/IEC 14882-2014]

Subclause 3.2, "One Definition Rule"
Subclause 7.1.2, "Function Specifiers"
Subclause 7.3.1, "Namespace Definition"

...


...

Image Modified Image Modified Image Modified