﻿# 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':

```cpp
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' и поместить их в то пространство имен, где определены классы:

```cpp
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, можно написать следующий код:

```cpp
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':

```cpp
namespace Foo
{
    class Bar
    {
      ....
    };
}

namespace std
{
  template <>
  struct hash<Foo::Bar>
  {
    size_t operator()(const Foo::Bar &) const noexcept;
  };
}
```

С точки зрения стандарта этот код является валидным, и анализатор в этой ситуации не выдает предупреждение\. Однако, начиная с C\+\+11, можно и в этом случае поступить иначе, написав специализацию шаблона класса за пределами пространства имен 'std':

```cpp
template <>
struct std::hash<Foo::Bar>
{
  size_t operator()(const Foo::Bar &) const noexcept;
};
```

В отличие от пространства имен 'std', стандарт C\+\+ запрещает абсолютно любую модификацию пространства имён 'posix':

```cpp
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