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;
}
}
Данная диагностика классифицируется как:
|
Взгляните на примеры ошибок, обнаруженных с помощью диагностики V1068. |