>
>
>
V826. Consider replacing standard conta…


V826. Consider replacing standard container with a different one.

Анализатор обнаружил контейнер из стандартной библиотеки C++, который можно заменить на другой контейнер в целях оптимизации.

Для определения, какой именно контейнер лучше подойдет в конкретном случае, применяется эвристика, основанная на типах операций, которые производят с контейнером. Кроме этого, анализатор вычисляет алгоритмическую сложность всех операций и предлагает контейнер, у которого она будет ниже.

В сообщении указывается причина замены:

  • "The size is known at compile time" – размер контейнера известен во время компиляции, возможна замена на статический массив (std::array).
  • "Elements are added, read and erased only from front/back" – контейнер реализует LIFO-очередь, можно использовать 'std::stack'.
  • "Elements are added to front/back, read and erased from the opposite side" – контейнер реализует FIFO-очередь, можно использовать 'std::queue'.
  • "Insertion and removal of elements occur at either side of the container" – элементы добавляются или удаляются с головы и с хвоста. В этом случае эффективно использовать 'std::deque' или 'std::list'.
  • "Insertions occur at the front side, and the container is traversed forward" – элементы добавляются только с головы и происходит обход в направлении хвоста. Контейнер используется, как 'std::forward_list'.
  • "Insertions occur at the back side, and the container is traversed" – элементы добавляются в хвост, происходит обход в любом направлении. Для такого использования подходит 'std::vector'.
  • "Contiguous placement of elements in memory can be more efficient" – если использовать 'std::vector', то алгоритмическая сложность операций не изменится, но последовательное расположение элементов в памяти может улучшить производительность.
  • "Overall efficiency of operations will increase" – контейнер выбран на основании статистического анализа.

Пример:

void f()
{
  std::vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);

  for (auto value : v)
  {
    std::cout << value << ' ';
  }
}

Анализатор выдает следующее сообщение:

V826. Consider replacing the 'v' std::vector with std::array. The size is known at compile time.

Здесь размер вектора известен во время компиляции. Если использовать вместо него 'std::array', можно избежать динамической аллокации памяти. Оптимизированный код:

void f()
{
  std::array a{1, 2, 3};
}

Анализатор не предлагает такую замену, если суммарный размер элементов превышает 16 килобайт, а также если вектор приходит в функцию снаружи, возвращается из функции, или передается в другую функцию параметром.

В следующем фрагменте кода сообщение не выдается несмотря на то, что размер контейнера известен:

std::vector<int> f()
{
  std::vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);

  return v;
}

Следующий пример, который можно оптимизировать:

void f(int n)
{
  std::vector<int> v;

  for (int i = 0; i < n; ++i)
  {
    v.push_back(i);
  }

  for (int i = 0; i < n; ++i)
  {
    std::cout << v.back() << ' ';
    v.pop_back();
  }
}

Анализатор выдает следующее сообщение:

V826. Consider replacing the 'v' std::vector with std::stack. Elements are added, read and erased only from front/back.

Здесь элементы добавляются в хвост вектора, а затем происходит их последовательное чтение и удаление. Вектор используется, как 'std::stack'. Можно произвести замену. Оптимизированный код:

void f(int n)
{
  std::stack<int> v;

  for (int i = 0; i < n; ++i)
  {
    v.push(i);
  }

  for (int i = 0; i < n; ++i)
  {
    std::cout << v.top() << ' ';
    v.pop();
  }
}

Следующий пример, который можно оптимизировать:

void f(int n)
{
  std::deque<int> d;
  for (int i = 0; i < n; i++)
  {
    d.push_back(i);
  }

  for (auto value : d)
  {
    std::cout << value << ' ';
  }
}

Анализатор выдает следующее сообщение:

V826. Consider replacing the 'd' std::deque with std::vector. Contiguous placement of elements in memory can be more efficient.

В этом случае 'std::deque' и 'std::vector' не отличаются с точки зрения алгоритмической сложности. Однако, в случае с вектором элементы в памяти будут расположены последовательно. Это может увеличить производительность, так как последовательный доступ к памяти позволяет эффективно использовать кэш процессора. Оптимизированный код:

void f(int n)
{
  std::vector<int> d;
  for (int i = 0; i < n; i++)
  {
    d.push_back(i);
  }

  for (auto value : d)
  {
    std::cout << value << ' ';
  }
}