Автономная, совместимая с STL реализация std::vector

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

void resize(size_type new_size, value_type value);

вместо подписи, совместимой с C++11, которая использовалась в большинстве других реализаций STL (например, STL или STLport gcc) задолго до 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 или gcc STL, но, будучи полностью совместимыми со стандартами, они оба большие и имеют множество нетривиальных зависимостей.

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

Любые идеи?


person François Beaune    schedule 23.02.2012    source источник
comment
Я не понимаю, как метод .resize() приводит к сбою объявления std::vector<S> v;. При создании экземпляра шаблона класса не создаются экземпляры его методов, а только используемые. (То есть ctor и dtor по умолчанию в этом случае).   -  person MSalters    schedule 23.02.2012
comment
Я предполагаю, что ошибка выдается во время синтаксического анализа... Имейте в виду, что здесь мы говорим о действительно специфичной для компилятора проблеме.   -  person François Beaune    schedule 23.02.2012


Ответы (2)


Создатели библиотеки 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...

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

person François Beaune    schedule 23.02.2012

Самым простым (и лучшим, имхо) вариантом было бы предоставить бесплатную функцию в качестве расширения интерфейса 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 и, следовательно, от аргументов шаблона.

person Xeo    schedule 23.02.2012
comment
Создание подкласса std::vector было одним из моих первых подходов, но, к сожалению, он все еще не компилируется, потому что, в конце концов, std::vector все еще создается экземпляр. - person François Beaune; 23.02.2012
comment
@FrançoisBeune: Ах, это относительно неудачно. :/ Попробуйте закомментировать версию std::vector или просто поправьте подпись. :P FWIW, VS11 по-прежнему не решает эту проблему. - person Xeo; 23.02.2012
comment
Странно, согласно STL здесь, vector::resize в VS11 должна иметь подпись T const&, но в текущем предварительном просмотре для разработчиков ее нет... Думаю, я напишу ему письмо. - person Xeo; 23.02.2012