# V1061. Extending 'std' or 'posix' namespace may result in undefined behavior.

Анализатор обнаружил расширение пространства имён 'std' или 'posix'. Несмотря на то, что такая программа успешно компилируется и исполняется, модификация данных пространств имён может привести к неопределённому поведению программы, если иное не указано стандартом.

Содержимое пространства имен 'std' определяется исключительно комитетом стандартизации, и стандарт запрещает добавлять в него:

- декларации переменных;
- декларации функций;
- декларации классов/структур/объединений;
- декларации перечислений;
- декларации шаблонов функций, классов и переменных (C++14);

Стандарт разрешает добавлять следующие специализации шаблонов, определенных в пространстве имен 'std', если они зависят хотя бы от одного определенного в программе типа (program-defined type):

- полная или частичная специализация шаблона класса;
- полная специализация шаблона функции (до C++20);
- полная или частичная специализация шаблона переменной, не лежащих в заголовочном файле '<type_traits>' (до C++20);

Однако, специализации шаблонов, лежащих внутри классов или шаблонов классов, запрещены.

Наиболее частым вариантом, когда пользователь расширяет пространство имен '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
    }

Дополнительная информация:

- Стандарт C++17 (working draft N4659), пункт 20.5.4.2.1
- Стандарт C++20 (working draft N4860), пункт 16.5.4.2.1

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

CERT-DCL58-CPP

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