>
>
>
V1068. Do not define an unnamed namespa…


V1068. Do not define an unnamed namespace in a header file.

Анализатор обнаружил анонимное пространство имен, объявленное в заголовочном файле. Такой заголовочный файл создаёт копии символов с "внутренним связыванием" (internal linkage) в каждой единице трансляции, включающей этот заголовочный файл. Это приводит к "раздуванию" объектных файлов, что может быть нежелательным поведением.

Рассмотрим простой пример заголовочного файла с анонимным пространством имен:

// utils.hpp
#pragma once
#include <iostream>

namespace
{
  int global_variable;
  void set_global_variable(int v)
  {
    std::cout << global_variable << std::endl;
    global_variable = v;
  }
}

Каждая единица трансляции при включении заголовочного файла 'utils.hpp' получит свой экземпляр переменной 'global_variable', не связанной с другими экземплярами и не доступной из других единиц трансляции. Также будет сгенерировано несколько избыточных функций 'set_global_variable'. До стандарта C++17 такой код мог встречаться в header-only библиотеках для того, чтобы не нарушать One Definition Rule при включении заголовочных файлов в несколько единиц трансляции. Также подобный код может появиться из-за неаккуратного рефакторинга, например, при переносе анонимного пространства имен из компилируемого файла в заголовочный файл.

Стоит отметить, что данное правило распространяется и на безымянные пространства имен, вложенные в другие пространства имен:

namespace my_namespace
{
  int variable1; // namespace-scope non-const variable
                 // 'variable1' has external linkage

  namespace // <=
  {
    int variable2; // unnamed namespace applies 'static'
                   // 'variable2' has internal linkage
  }
}

Если необходимо создать ровно один экземпляр символа для header-only библиотеки, то можно воспользоваться спецификатором 'inline' (начиная с C++17, действует и для переменных):

// utils.hpp
#pragma once
#include <iostream>

inline int global_variable; // ok since C++17

inline void set_global_variable(int v)
{
  std::cout << global_variable << std::endl;
  global_variable = v;
}

Если используется более ранняя версия стандарта, но библиотека не header-only, то можно объявить символы как 'extern' в заголовочном файле и определить их в одном из юнитов трансляции:

// utils.hpp
#pragma once

extern int global_variable;
void set_global_variable(int v); // functions implicitly 
                                 // have external linkage ('extern')

// utils.cpp
#include "utils.hpp"
#include <iostream>

int global_variable;

void set_global_variable(int v)
{
  std::cout << global_variable << std::endl;
  global_variable = v;
}

В том случае, когда используется более старая версия стандарта, но библиотека должна быть header-only, срабатывание можно подавить комментарием:

// utils.hpp
#pragma once
#include <iostream>

namespace //-V1068
{
  int global_variable;
  void set_global_variable(int v)
  {
    std::cout << global_variable << std::endl;
    global_variable = v;
  }
}

Данная диагностика классифицируется как:

  • CERT-DCL59-CPP

Взгляните на примеры ошибок, обнаруженных с помощью диагностики V1068.