Я работаю с библиотекой линейной алгебры Eigen и мне нужен вектор BiCGSTAB
-решателей. К сожалению, расширить этот вектор крайне сложно. Минимальный (не) рабочий пример:
#include <Eigen/Eigen>
int main() {
std::vector< Eigen::BiCGSTAB< Eigen::SparseMatrix< double > > > tmp;
tmp.emplace_back();
}
и выдает сообщение об ошибке
$ g++ -I/usr/include/eigen3 main.cpp
In file included from /usr/include/c++/12.2.0/vector:63,
from /usr/include/c++/12.2.0/functional:62,
from /usr/include/eigen3/Eigen/Core:85,
from /usr/include/eigen3/Eigen/Dense:1,
from /usr/include/eigen3/Eigen/Eigen:1,
from main.cpp:1:
/usr/include/c++/12.2.0/bits/stl_uninitialized.h: In instantiation of ‘constexpr bool std::__check_constructible() [with _ValueType = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >; _Tp = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >&&]’:
/usr/include/c++/12.2.0/bits/stl_uninitialized.h:182:4: required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = move_iterator<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >*>; _ForwardIterator = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >*]’
/usr/include/c++/12.2.0/bits/stl_uninitialized.h:372:37: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, allocator<_Tp>&) [with _InputIterator = move_iterator<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >*>; _ForwardIterator = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >*; _Tp = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >]’
/usr/include/c++/12.2.0/bits/stl_uninitialized.h:397:2: required from ‘_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >*; _ForwardIterator = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >*; _Allocator = allocator<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >]’
/usr/include/c++/12.2.0/bits/vector.tcc:487:3: required from ‘void std::vector<_Tp, _Alloc>::_M_realloc_insert(iterator, _Args&& ...) [with _Args = {}; _Tp = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >; _Alloc = std::allocator<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >; iterator = std::vector<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >::iterator]’
/usr/include/c++/12.2.0/bits/vector.tcc:123:21: required from ‘std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {}; _Tp = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >; _Alloc = std::allocator<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >; reference = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >&]’
main.cpp:5:21: required from here
/usr/include/c++/12.2.0/bits/stl_uninitialized.h:90:56: error: static assertion failed: result type must be constructible from input type
90 | static_assert(is_constructible<_ValueType, _Tp>::value,
| ^~~~~
/usr/include/c++/12.2.0/bits/stl_uninitialized.h:90:56: note: ‘std::integral_constant<bool, false>::value’ evaluates to false
Пытаться std::move
хуже, т.е.
#include <Eigen/Eigen>
#include <utility>
int main() {
std::vector< Eigen::BiCGSTAB< Eigen::SparseMatrix< double > > > tmp;
Eigen::BiCGSTAB< Eigen::SparseMatrix< double > > solver;
tmp.push_back( std::move( solver ) );
}
приводит к сообщению об ошибке
g++ -I/usr/include/eigen3 main.cpp
In file included from /usr/include/c++/12.2.0/x86_64-pc-linux-gnu/bits/c++allocator.h:33,
from /usr/include/c++/12.2.0/bits/allocator.h:46,
from /usr/include/c++/12.2.0/string:41,
from /usr/include/c++/12.2.0/bits/locale_classes.h:40,
from /usr/include/c++/12.2.0/bits/ios_base.h:41,
from /usr/include/c++/12.2.0/ios:42,
from /usr/include/c++/12.2.0/istream:38,
from /usr/include/c++/12.2.0/sstream:38,
from /usr/include/c++/12.2.0/complex:45,
from /usr/include/eigen3/Eigen/Core:50,
from /usr/include/eigen3/Eigen/Dense:1,
from /usr/include/eigen3/Eigen/Eigen:1,
from main.cpp:1:
/usr/include/c++/12.2.0/bits/new_allocator.h: In instantiation of ‘void std::__new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >; _Args = {Eigen::BiCGSTAB<Eigen::SparseMatrix<double, 0, int>, Eigen::DiagonalPreconditioner<double> >}; _Tp = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >]’:
/usr/include/c++/12.2.0/bits/alloc_traits.h:516:17: required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(allocator_type&, _Up*, _Args&& ...) [with _Up = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >; _Args = {Eigen::BiCGSTAB<Eigen::SparseMatrix<double, 0, int>, Eigen::DiagonalPreconditioner<double> >}; _Tp = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >; allocator_type = std::allocator<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >]’
/usr/include/c++/12.2.0/bits/vector.tcc:117:30: required from ‘std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {Eigen::BiCGSTAB<Eigen::SparseMatrix<double, 0, int>, Eigen::DiagonalPreconditioner<double> >}; _Tp = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >; _Alloc = std::allocator<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >; reference = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >&]’
/usr/include/c++/12.2.0/bits/stl_vector.h:1294:21: required from ‘void std::vector<_Tp, _Alloc>::push_back(value_type&&) [with _Tp = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >; _Alloc = std::allocator<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >; value_type = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >]’
main.cpp:9:18: required from here
/usr/include/c++/12.2.0/bits/new_allocator.h:175:11: error: use of deleted function ‘Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >::BiCGSTAB(const Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >&)’
175 | { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/eigen3/Eigen/IterativeLinearSolvers:42,
from /usr/include/eigen3/Eigen/Sparse:31,
from /usr/include/eigen3/Eigen/Eigen:2:
/usr/include/eigen3/Eigen/src/IterativeLinearSolvers/BiCGSTAB.h:158:7: note: ‘Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >::BiCGSTAB(const Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >&)’ is implicitly deleted because the default definition would be ill-formed:
158 | class BiCGSTAB : public IterativeSolverBase<BiCGSTAB<_MatrixType,_Preconditioner> >
| ^~~~~~~~
/usr/include/eigen3/Eigen/src/IterativeLinearSolvers/BiCGSTAB.h:158:7: error: use of deleted function ‘Eigen::IterativeSolverBase<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >::IterativeSolverBase(const Eigen::IterativeSolverBase<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >&)’
In file included from /usr/include/eigen3/Eigen/IterativeLinearSolvers:38:
/usr/include/eigen3/Eigen/src/IterativeLinearSolvers/IterativeSolverBase.h:143:7: note: ‘Eigen::IterativeSolverBase<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >::IterativeSolverBase(const Eigen::IterativeSolverBase<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >&)’ is implicitly deleted because the default definition would be ill-formed:
143 | class IterativeSolverBase : public SparseSolverBase<Derived>
| ^~~~~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/IterativeLinearSolvers/IterativeSolverBase.h:143:7: error: use of deleted function ‘Eigen::SparseSolverBase<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >::SparseSolverBase(const Eigen::SparseSolverBase<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >&)’
In file included from /usr/include/eigen3/Eigen/SparseCore:64,
from /usr/include/eigen3/Eigen/Sparse:26:
/usr/include/eigen3/Eigen/src/SparseCore/SparseSolverBase.h:67:7: note: ‘Eigen::SparseSolverBase<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >::SparseSolverBase(const Eigen::SparseSolverBase<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >&)’ is implicitly deleted because the default definition would be ill-formed:
67 | class SparseSolverBase : internal::noncopyable
| ^~~~~~~~~~~~~~~~
/usr/include/eigen3/Eigen/src/SparseCore/SparseSolverBase.h:67:7: error: ‘Eigen::internal::noncopyable::noncopyable(const Eigen::internal::noncopyable&)’ is private within this context
In file included from /usr/include/eigen3/Eigen/Core:162:
/usr/include/eigen3/Eigen/src/Core/util/Meta.h:424:21: note: declared private here
424 | EIGEN_DEVICE_FUNC noncopyable(const noncopyable&);
| ^~~~~~~~~~~
In file included from /usr/include/c++/12.2.0/vector:63,
from /usr/include/c++/12.2.0/functional:62,
from /usr/include/eigen3/Eigen/Core:85:
/usr/include/c++/12.2.0/bits/stl_uninitialized.h: In instantiation of ‘constexpr bool std::__check_constructible() [with _ValueType = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >; _Tp = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >&&]’:
/usr/include/c++/12.2.0/bits/stl_uninitialized.h:182:4: required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = move_iterator<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >*>; _ForwardIterator = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >*]’
/usr/include/c++/12.2.0/bits/stl_uninitialized.h:372:37: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, allocator<_Tp>&) [with _InputIterator = move_iterator<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >*>; _ForwardIterator = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >*; _Tp = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >]’
/usr/include/c++/12.2.0/bits/stl_uninitialized.h:397:2: required from ‘_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >*; _ForwardIterator = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >*; _Allocator = allocator<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >]’
/usr/include/c++/12.2.0/bits/vector.tcc:487:3: required from ‘void std::vector<_Tp, _Alloc>::_M_realloc_insert(iterator, _Args&& ...) [with _Args = {Eigen::BiCGSTAB<Eigen::SparseMatrix<double, 0, int>, Eigen::DiagonalPreconditioner<double> >}; _Tp = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >; _Alloc = std::allocator<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >; iterator = std::vector<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >::iterator]’
/usr/include/c++/12.2.0/bits/vector.tcc:123:21: required from ‘std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {Eigen::BiCGSTAB<Eigen::SparseMatrix<double, 0, int>, Eigen::DiagonalPreconditioner<double> >}; _Tp = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >; _Alloc = std::allocator<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >; reference = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >&]’
/usr/include/c++/12.2.0/bits/stl_vector.h:1294:21: required from ‘void std::vector<_Tp, _Alloc>::push_back(value_type&&) [with _Tp = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >; _Alloc = std::allocator<Eigen::BiCGSTAB<Eigen::SparseMatrix<double> > >; value_type = Eigen::BiCGSTAB<Eigen::SparseMatrix<double> >]’
main.cpp:9:18: required from here
/usr/include/c++/12.2.0/bits/stl_uninitialized.h:90:56: error: static assertion failed: result type must be constructible from input type
90 | static_assert(is_constructible<_ValueType, _Tp>::value,
| ^~~~~
/usr/include/c++/12.2.0/bits/stl_uninitialized.h:90:56: note: ‘std::integral_constant<bool, false>::value’ evaluates to false
Я использую Eigen 3.4 и g++
версию 12.2.
Любые идеи, как это исправить?
Спасибо @ Homer512, я не знал, что std::dequeue
разрешает произвольный доступ. Тогда это подойдет.
И если вы заранее знаете, сколько решателей вам нужно, std::unique_ptr<Eigen::BICGSTAB<...>[]>
тоже работает. Обычный старый синтаксис new Array[n]
. Не забудьте []
в шаблоне указателя, чтобы получить специализацию массива.
Превращая мои комментарии в правильный ответ:
Изучив код, я обнаружил, что BiCGSTAB
, как и все решатели, наследуется от базового класса, предназначенного для предотвращения копирования и, соответственно, перемещения: class SparseSolverBase : internal::noncopyable
Точных причин выбора именно этого дизайна я не могу сказать. Если бы мне пришлось угадывать, я бы сказал, что некоторые решатели, вероятно, используют самореферентные атрибуты (удерживающие указатели на другие элементы), которые будут ломаться, особенно с матрицами фиксированного размера. Или использование Eigen::Map
может вызвать проблемы при копировании, особенно при назначении копирования.
std::vector
работает только с подвижными типами, так как он должен двигаться при перераспределении. Даже при предварительном вызове reserve()
код все равно необходимо скомпилировать, даже если он никогда не выполняется.
На ум приходят три обходных пути:
Используйте std::deque
. Он предоставляет расширенный набор всех методов, которые имеет вектор, но его реализация означает, что до тех пор, пока вы вызываете только emplace_back
или emplace_front
, а не, например. insert
, ему не нужны подвижные типы. Недостатком является то, что он немного медленнее для всех отдельных доступов.
Используйте std::vector<std::unique_ptr<Solver>>
. Менее эффективен, чем дек, но теперь вы также можете вставлять, перетасовывать и т. д.
Используйте std::unique_ptr<Solver[]>
и используйте старое доброе распределение new Solver[count]
. Начиная с C++14, вы можете использовать std::make_unique<Solver[]>(count)
. Это имеет наименьшие накладные расходы, даже меньше, чем вектор, но интерфейс не такой приятный (вы можете использовать оператор [index]
, но указатель даже не знает размер массива), а число фиксируется после выделения
Похоже, что базовый класс подавляет копирование/перемещение этих объектов, которые нужны вектору, если он хочет изменить свой размер. Если переключиться с вектора на
std::deque
, тоemplace_back()
работает.