Автономная, STL-совместимая реализация std :: vector - PullRequest
7 голосов
/ 23 февраля 2012

Реализация std::vector, поставляемая с Visual Studio 2010 и более ранними версиями, имеет хорошо известную особенность: метод resize имеет следующую сигнатуру (совместимую с C ++ 03):

void resize(size_type new_size, value_type value);

вместо C ++ 11-совместимой подписи, которая использовалась большинством других реализаций STL (например, gcc STL или STLport) задолго до C ++ 11:

void resize(size_type new_size, const value_type& value);

Проблема с первым вариантомявляется то, что в некоторых ситуациях он не сможет скомпилироваться, если value_type имеет спецификацию выравнивания:

struct __declspec(align(64)) S { ... };
std::vector<S> v;  // error C2719: '_Val': formal parameter with __declspec(align('64')) won't be aligned

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

Я ищу хорошо написанную, хорошо протестированную, автономную и совместимую с STL реализацию std::vector сЛицензия в стиле MIT , которую я могу добавить в свой проект в качестве контейнера выбора для выровненных типов.

Я подумал о том, чтобы извлечь его из STLport или STL gcc, но, будучи полностью совместимым со стандартом, thОни оба велики со многими нетривиальными зависимостями.

(Я был бы совершенно счастлив, если бы реализовал разумное подмножество std::vector, которое поддерживало бы только push_back, clear, capacity, size, reserve, resize, swap и индексация массива.)

Есть идеи?

Ответы [ 2 ]

8 голосов
/ 23 февраля 2012

Ребята из библиотеки Eigen , похоже, нашли хороший обходной путь для проблемы хранения "чрезмерно выровненных типов" (, как Стефан Т. Лававей называет их ) вstd::vector как реализовано в Visual Studio STL.

Их реализация кажется ненужной сложной (проверьте источники здесь и здесь ), но основная идея заключается в инкапсуляции типакоторый входит в std::vector с тонкой оболочкой:

#include <vector>

template <typename T>
struct wrapper : public T
{
    wrapper() {}
    wrapper(const T& rhs) : T(rhs) {}
};

struct __declspec(align(64)) S
{
    float x, y, z, w;
};

int main()
{
    std::vector< wrapper<S> > v;  // OK, no C2719 error
    return 0;
}

По поводу реализации в Eigen, должен признать, я не совсем понимаю

  • зачем им нужно Eigen::aligned_allocator_indirection,
  • почему им нужно сделать исключение для арифметических типов в EIGEN_WORKAROUND_MSVC_STL_SUPPORT,
  • , почему им нужно определить все эти конструкторы и операторы в Eigen::workaround_msvc_stl_support,
  • или почему им нужно переопределить resize в их частичной специализации std::vector для Eigen::aligned_allocator_indirection распределителя ...

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

1 голос
/ 23 февраля 2012

Самый простой (и лучший, imho) вариант - предоставить бесплатную функцию в качестве расширения интерфейса vector, которая делает правильные вещи. Все необходимые вам функции resize доступны из открытого интерфейса std::vector:

#include <vector>

template<class T, class Alloc>
void resize(std::vector<T, Alloc>& v,
    typename std::vector<T, Alloc>::size_type new_size, T const& val)
{
  if (v.size() < new_size)
      v.insert(v.end(), new_size - v.size(), val);
  else if (new_size < v.size())
      v.erase(v.begin() + new_size, v.end());
}

И для согласованности также версия с одним аргументом:

template<class T, class Alloc>
void resize(std::vector<T, Alloc>& v,
    typename std::vector<T, Alloc>::size_type new_size)
{
    v.resize(new_size); // simply forward
}

Если, однако, вы просто хотите добавить новый вектор и никогда не беспокоиться о свободной функции или функции-члене, другой вариант - просто создать подкласс std::vector:

#include <vector>
#include <memory>

template<class T, class Alloc = std::allocator<T>>
class myvector
  : public std::vector<T, Alloc>
{
  typedef std::vector<T, Alloc> base;
public:
  typedef typename base::size_type size_type;

  void resize(size_type new_size){
    base::resize(new_size);
  }

  void resize(size_type new_size, T const& val){
    if (this->size() < new_size)
        this->insert(this->end(), new_size - this->size(), val);
    else if (new_size < this->size())
        this->erase(this->begin() + new_size, this->end());
  }
};

Обратите внимание, что я также предоставил версию с одним аргументом resize, поскольку версия с двумя аргументами скрывала бы все версии базового класса. Также обратите внимание, что мне нужно было поставить перед всеми вызовами функций-членов префикс this->, поскольку они зависят от базового класса std::vector и как таковые от аргументов шаблона.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...