Анализатор обнаружил расширение пространства имён 'std' или 'posix'. Несмотря на то, что такая программа успешно компилируется и исполняется, модификация данных пространств имён может привести к неопределённому поведению программы, если иное не указано стандартом.
Содержимое пространства имен 'std' определяется исключительно комитетом стандартизации, и стандарт запрещает добавлять в него:
Стандарт разрешает добавлять следующие специализации шаблонов, определенных в пространстве имен 'std', если они зависят хотя бы от одного определенного в программе типа (program-defined type):
Однако, специализации шаблонов, лежащих внутри классов или шаблонов классов, запрещены.
Наиболее частым вариантом, когда пользователь расширяет пространство имен 'std', является добавление своей перегрузки функции 'std::swap' и полной/частичной специализации шаблона класса 'std::hash'.
Рассмотрим фрагмент кода с добавлением перегрузки 'std::swap':
template <typename T> class MyTemplateClass { .... }; class MyClass { .... }; namespace std { template <typename T> void swap(MyTemplateClass<T> &a, MyTemplateClass<T> &b) noexcept // UB { .... } template <> void swap(MyClass &a, MyClass &b) noexcept // UB since C++20 { .... }; }
Первый шаблон функции не является специализацией 'std::swap', и такая декларация ведет к неопределенному поведению. Второй шаблон функции является специализацией, и до C++20 поведение программы определено. Однако, в данном случае можно поступить иначе: можно вынести обе функции из пространства имен 'std' и поместить их в то пространство имен, где определены классы:
template <typename T> class MyTemplateClass { .... }; class MyClass { .... }; template <typename T> void swap(MyTemplateClass<T> &a, MyTemplateClass<T> &b) noexcept { .... } void swap(MyClass &a, MyClass &b) noexcept { .... };
Теперь, когда необходимо написать шаблон функции, который применяет функцию swap для двух объектов типа T, можно написать следующий код:
template <typename T> void MyFunction(T& obj1, T& obj2) { using std::swap; // make std::swap visible for overload resolution .... swap(obj1, obj2); // best match of 'swap' for objects of type T .... }
Теперь, компилятор выберет нужную перегрузку функции на основе поиска с учетом аргументов (argument-dependent lookup, ADL) – пользовательские функции 'swap' для класса 'MyClass' и для шаблона класса 'MyTemplateClass' и стандартную версию 'std::swap' для остальных типов.
Разберем следующий пример со специализацией шаблона класса 'std::hash':
namespace Foo { class Bar { .... }; } namespace std { template <> struct hash<Foo::Bar> { size_t operator()(const Foo::Bar &) const noexcept; }; }
С точки зрения стандарта этот код является валидным, и анализатор в этой ситуации не выдает предупреждение. Однако, начиная с C++11, можно и в этом случае поступить иначе, написав специализацию шаблона класса за пределами пространства имен 'std':
template <> struct std::hash<Foo::Bar> { size_t operator()(const Foo::Bar &) const noexcept; };
В отличие от пространства имен 'std', стандарт C++ запрещает абсолютно любую модификацию пространства имён 'posix':
namespace posix { int x; // UB }
Дополнительная информация:
Данная диагностика классифицируется как:
|
Взгляните на примеры ошибок, обнаруженных с помощью диагностики V1061. |