Почему списки инициализаторов недоступны при смене распределителя std::vector?

В моем проекте я изменил используемый тип точки от Eigen::Vector2f до Eigen::Vector2d и столкнулся с проблемой выравнивания.

Вот упрощенная версия кода:

#include <vector>
#include <Eigen/Eigen>

int main()
{
    std::vector<Eigen::Vector2d> points = { {0,0}, {0,1} };
}

Я получаю следующую ошибку времени выполнения:

eigen3/Eigen/src/Core/DenseStorage.h:78: Eigen::internal::plain_array<double, 2, 0, 16>::plain_array() [T = double, Size = 2, MatrixOrArrayOptions = 0, Alignment = 16]: Assertion `(reinterpret_cast<size_t>(array) & 0xf) == 0 && "this assertion is explained here: " "http://eigen.tuxfamily.org/dox-devel/group__TopicUnalignedArrayAssert.html" " **** READ THIS WEB PAGE !!! ****"' failed.

В качестве предложения assert-сообщения я прочитал о требуемом выравнивании объектов Eigen с фиксированным размером. А также подраздел о STL Containers. И похоже, что у меня есть два варианта:

  • используйте Eigen::aligned_allocator
  • или используйте макрос EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION.

Обе попытки не компилируются (протестированы с помощью GCC 4.8.3 и Clang 3.5), потому что компилятор не может правильно преобразовать список инициализаторов.

Здесь измененный код:

#include <vector>
#include <Eigen/Eigen>
#include <Eigen/StdVector>
// EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Eigen::Vector2d)

int main()
{
    std::vector<Eigen::Vector2d, Eigen::aligned_allocator<Eigen::Vector2d>> points = { {0,0}, {0,1} };
    // std::vector<Eigen::Vector2d> points = { {0,0}, {0,1} };
}

Выход ошибки GCC:

error: could not convert ‘{{0, 0}, {0, 1}}’ from ‘<brace-enclosed initializer list>’ to ‘std::vector<Eigen::Matrix<double, 2, 1>, Eigen::aligned_allocator<Eigen::Matrix<double, 2, 1> > >’

Итак, мне интересно:

  • Почему списки инициализаторов недоступны при смене распределителя std::vector?

    • Это из-за выравнивания?
    • Можно ли каким-либо образом выровнять список инициализаторов?
  • Почему версия специализации не работает?

    • У них отсутствует функция списка инициализаторов?

Ответы

Ответ 1

Изучив файл include Eigen/StdVector (точнее, он находится в Eigen/src/StlSupport/StdVector.h строка 68 версии 3.2.1), он кажется, что проблема возникает из частичной специализированной специализации std::vector в этом заголовочном файле. Эта спецификация частичного шаблона заменяет STL vector, как только вы используете Eigen::aligned_allocator в качестве распределителя. И эта специализация, похоже, не имеет возможностей С++ 11.

Подробно, почему эта специализация необходима, кроме замены распределителя: Перед С++ 11 функция изменения размера в std::vector может принимать дополнительный параметр по значению для инициализации вновь созданных элементов. Согласно документации Eigen3 передача параметра по значению отбрасывает любые модификаторы выравнивания и не может использоваться с объектами Eigen с фиксированным размером (см. SIMD).

Edit: После некоторого тестирования я понял, что реализация С++ 11 std::vector не имеет указанной выше проблемы. Поэтому, чтобы решить проблему выравнивания, вы только должны заполнить Eigen::aligned_allocator. Но не включать Eigen/StdVector. Включение этого файла не позволит вам использовать реализацию С++ 11 std::vector, поскольку этот заголовок определяет частичную специализацию с Eigen::aligned_allocator как распределитель.