stl — Как объявить вектор атома в переполнении стека

Я намерен объявить вектор атомарных переменных для использования в качестве счетчиков в многопоточной программе. Вот что я попробовал:

#include <atomic>
#include <vector>

int main(void)
{
std::vector<std::atomic<int>> v_a;
std::atomic<int> a_i(1);
v_a.push_back(a_i);
return 0;
}

И это досадно подробное сообщение об ошибке gcc 4.6.3:

In file included from /usr/include/c++/4.6/x86_64-linux-gnu/./bits/c++allocator.h:34:0,
from /usr/include/c++/4.6/bits/allocator.h:48,
from /usr/include/c++/4.6/vector:62,
from test_atomic_vec.h:2,
from test_atomic_vec.cc:1:
/usr/include/c++/4.6/ext/new_allocator.h: In member function ‘void __gnu_cxx::new_allocator<_Tp>::construct(__gnu_cxx::new_allocator<_Tp>::pointer, const _Tp&) [with _Tp = std::atomic<int>, __gnu_cxx::new_allocator<_Tp>::pointer = std::atomic<int>*]’:
/usr/include/c++/4.6/bits/stl_vector.h:830:6:   instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::value_type = std::atomic<int>]’
test_atomic_vec.cc:10:20:   instantiated from here
/usr/include/c++/4.6/ext/new_allocator.h:108:9: error: use of deleted function ‘std::atomic<int>::atomic(const std::atomic<int>&)’
/usr/include/c++/4.6/atomic:538:7: error: declared here
In file included from /usr/include/c++/4.6/vector:70:0,
from test_atomic_vec.h:2,
from test_atomic_vec.cc:1:
/usr/include/c++/4.6/bits/vector.tcc: In member function ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {const std::atomic<int>&}, _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<std::atomic<int>*, std::vector<std::atomic<int> > >, typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer = std::atomic<int>*]’:
/usr/include/c++/4.6/bits/stl_vector.h:834:4:   instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::value_type = std::atomic<int>]’
test_atomic_vec.cc:10:20:   instantiated from here
/usr/include/c++/4.6/bits/vector.tcc:319:4: error: use of deleted function ‘std::atomic<int>::atomic(const std::atomic<int>&)’
/usr/include/c++/4.6/atomic:538:7: error: declared here
/usr/include/c++/4.6/bits/stl_vector.h:834:4:   instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::value_type = std::atomic<int>]’
test_atomic_vec.cc:10:20:   instantiated from here
/usr/include/c++/4.6/bits/vector.tcc:319:4: error: use of deleted function ‘std::atomic<int>& std::atomic<int>::operator=(const std::atomic<int>&)’
/usr/include/c++/4.6/atomic:539:15: error: declared here
In file included from /usr/include/c++/4.6/x86_64-linux-gnu/./bits/c++allocator.h:34:0,
from /usr/include/c++/4.6/bits/allocator.h:48,
from /usr/include/c++/4.6/vector:62,
from test_atomic_vec.h:2,
from test_atomic_vec.cc:1:
/usr/include/c++/4.6/ext/new_allocator.h: In member function ‘void __gnu_cxx::new_allocator<_Tp>::construct(__gnu_cxx::new_allocator<_Tp>::pointer, _Args&& ...) [with _Args = {std::atomic<int>}, _Tp = std::atomic<int>, __gnu_cxx::new_allocator<_Tp>::pointer = std::atomic<int>*]’:
/usr/include/c++/4.6/bits/vector.tcc:306:4:   instantiated from ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {const std::atomic<int>&}, _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<std::atomic<int>*, std::vector<std::atomic<int> > >, typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer = std::atomic<int>*]’
/usr/include/c++/4.6/bits/stl_vector.h:834:4:   instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::value_type = std::atomic<int>]’
test_atomic_vec.cc:10:20:   instantiated from here
/usr/include/c++/4.6/ext/new_allocator.h:114:4: error: use of deleted function ‘std::atomic<int>::atomic(const std::atomic<int>&)’
/usr/include/c++/4.6/atomic:538:7: error: declared here
In file included from /usr/include/c++/4.6/vector:61:0,
from test_atomic_vec.h:2,
from test_atomic_vec.cc:1:
/usr/include/c++/4.6/bits/stl_algobase.h: In static member function ‘static _BI2 std::__copy_move_backward<true, false, std::random_access_iterator_tag>::__copy_move_b(_BI1, _BI1, _BI2) [with _BI1 = std::atomic<int>*, _BI2 = std::atomic<int>*]’:
/usr/include/c++/4.6/bits/stl_algobase.h:581:18:   instantiated from ‘_BI2 std::__copy_move_backward_a(_BI1, _BI1, _BI2) [with bool _IsMove = true, _BI1 = std::atomic<int>*, _BI2 = std::atomic<int>*]’
/usr/include/c++/4.6/bits/stl_algobase.h:590:34:   instantiated from ‘_BI2 std::__copy_move_backward_a2(_BI1, _BI1, _BI2) [with bool _IsMove = true, _BI1 = std::atomic<int>*, _BI2 = std::atomic<int>*]’
/usr/include/c++/4.6/bits/stl_algobase.h:661:15:   instantiated from ‘_BI2 std::move_backward(_BI1, _BI1, _BI2) [with _BI1 = std::atomic<int>*, _BI2 = std::atomic<int>*]’
/usr/include/c++/4.6/bits/vector.tcc:313:4:   instantiated from ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {const std::atomic<int>&}, _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<std::atomic<int>*, std::vector<std::atomic<int> > >, typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer = std::atomic<int>*]’
/usr/include/c++/4.6/bits/stl_vector.h:834:4:   instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::value_type = std::atomic<int>]’
test_atomic_vec.cc:10:20:   instantiated from here
/usr/include/c++/4.6/bits/stl_algobase.h:546:6: error: use of deleted function ‘std::atomic<int>& std::atomic<int>::operator=(const std::atomic<int>&)’
/usr/include/c++/4.6/atomic:539:15: error: declared here
In file included from /usr/include/c++/4.6/vector:63:0,
from test_atomic_vec.h:2,
from test_atomic_vec.cc:1:
/usr/include/c++/4.6/bits/stl_construct.h: In function ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = std::atomic<int>, _Args = {std::atomic<int>}]’:
/usr/include/c++/4.6/bits/stl_uninitialized.h:77:3:   instantiated from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<std::atomic<int>*>, _ForwardIterator = std::atomic<int>*, bool _TrivialValueTypes = false]’
/usr/include/c++/4.6/bits/stl_uninitialized.h:119:41:   instantiated from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<std::atomic<int>*>, _ForwardIterator = std::atomic<int>*]’
/usr/include/c++/4.6/bits/stl_uninitialized.h:259:63:   instantiated from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::move_iterator<std::atomic<int>*>, _ForwardIterator = std::atomic<int>*, _Tp = std::atomic<int>]’
/usr/include/c++/4.6/bits/stl_uninitialized.h:269:24:   instantiated from ‘_ForwardIterator std::__uninitialized_move_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = std::atomic<int>*, _ForwardIterator = std::atomic<int>*, _Allocator = std::allocator<std::atomic<int> >]’
/usr/include/c++/4.6/bits/vector.tcc:343:8:   instantiated from ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {const std::atomic<int>&}, _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<std::atomic<int>*, std::vector<std::atomic<int> > >, typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer = std::atomic<int>*]’
/usr/include/c++/4.6/bits/stl_vector.h:834:4:   instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::atomic<int>, _Alloc = std::allocator<std::atomic<int> >, std::vector<_Tp, _Alloc>::value_type = std::atomic<int>]’
test_atomic_vec.cc:10:20:   instantiated from here
/usr/include/c++/4.6/bits/stl_construct.h:76:7: error: use of deleted function ‘std::atomic<int>::atomic(const std::atomic<int>&)’
/usr/include/c++/4.6/atomic:538:7: error: declared here

Как мне это решить?

Ошибка исчезает, когда я закомментирую строку с push_back() ,

редактировать: Я редактировал пост … Для тех из вас, кто видел первый пост, ошибка была смущающей, что я использовал gcc вместо g++ : \

28

Решение

Как описано в этом тесно связанный вопрос это было упомянуто в комментариях, std::atomic<T> не копируемый и не копируемый.

Типы объектов, которые не имеют этих свойств, не могут использоваться в качестве элементов std::vector,

Тем не менее, должно быть возможно создать обертку вокруг std::atomic<T> элемент, который является копируемым и назначаемым для копирования. Придется использовать load() а также store() функции-члены std::atomic<T> обеспечить строительство и назначение (это идея, описанная в принятом ответе на вопрос, упомянутый выше):

#include <atomic>
#include <vector>

template <typename T>
struct atomwrapper
{
std::atomic<T> _a;

atomwrapper()
:_a()
{}

atomwrapper(const std::atomic<T> &a)
:_a(a.load())
{}

atomwrapper(const atomwrapper &other)
:_a(other._a.load())
{}

atomwrapper &operator=(const atomwrapper &other)
{
_a.store(other._a.load());
}
};

int main(void)
{
std::vector<atomwrapper<int>> v_a;
std::atomic<int> a_i(1);
v_a.push_back(a_i);
return 0;
}

РЕДАКТИРОВАТЬ: Как правильно указал Бо Перссон, операция копирования, выполняемая оболочкой не является атомарным. Это позволяет вам копировать атомарные объекты, но сама копия не является атомарной. Это означает, что любой параллельный доступ к элементам не должен использовать операцию копирования. Это подразумевает, что операции над самим вектором (например, добавление или удаление элементов) не должны выполняться одновременно.

Пример: если, скажем, один поток изменяет значение, хранящееся в одной из атомик, в то время как другой поток добавляет новые элементы в вектор, может произойти перераспределение вектора, и объект, который модифицирует первый поток, может быть скопирован из одного места в векторе в другое , В этом случае может быть гонка данных между доступом к элементу, выполняемым первым потоком, и операцией копирования, запускаемой вторым.

16

Другие решения

Выглядит как atomic<T> не имеет конструктора копирования. Насколько я могу судить, ни на шаг конструктора.

Один обходной путь может быть использовать vector<T>::emplace_back() построить атом на месте в векторе. Увы, сейчас у меня нет компилятора C ++ 11, или я бы пошел и протестировал его.

7

Чтобы сначала ответить на ваш главный вопрос: вы можете объявлять std::vector<std::atomic<...>> легко, как вы сделали в своем примере.

Из-за отсутствия копирования или перемещения конструкторов для std::atomic<> объекты, однако, ваше использование vector будет ограничено, как вы узнали с ошибкой компиляции на push_back(), По сути, вы не можете сделать ничего, что вызвало бы любой конструктор.

Это означает, что размер вашего вектора должен быть зафиксирован при построении, и вы должны манипулировать элементами, используя operator[] или же .at(), Для вашего примера кода работает следующее1:

std::vector<std::atomic<int>> v_a(1);
std::atomic<int> a_i(1);
v_a[0] = a_i;

Если ограничение «фиксированный размер при строительстве» слишком обременительно, вы можете использовать std::deque вместо. Это позволяет вам устанавливать объекты, динамически увеличивающие структуру без необходимости копирования или перемещения конструкторов, например:

std::deque<std::atomic<int>> d;

d.emplace_back(1);
d.emplace_back(2);
d.pop_back();

Однако есть некоторые ограничения. Например, вы можете pop_back(), но вы не можете использовать более общий erase(), Ограничения имеют смысл: erase() в середине блоков непрерывного хранения, используемых std::deque в общем случае требует перемещения элементов, поэтому требует наличия конструктора копирования / перемещения или операторов присваивания.

Если вы не можете жить с этими ограничениями, вы можете создать класс-оболочку, как предложено в других ответах, но помните о базовой реализации: он делает мало смысла переместить std::atomic<> объект, если он используется: он прерывает любые потоки, одновременно обращающиеся к объектам. Единственное разумное использование конструкторов копирования / перемещения, как правило, заключается в начальной настройке коллекций этих объектов перед их публикацией в других потоках.


1 Если, возможно, вы не используете Intel icc компилятор, который не удается из-за внутренней ошибки во время компиляции этого кода.

3

Как правильно заметили другие, причиной ошибки компилятора является то, что std :: atomic явно запрещает конструктор копирования.

У меня был случай, когда я хотел удобство карты STL (в частности, я использовал карту карт, чтобы получить разреженную двумерную матрицу атомов, чтобы я мог сделать что-то вроде int val = my_map[10][5]). В моем случае в программе будет только один экземпляр этой карты, поэтому он не будет скопирован, и даже лучше, я могу инициализировать все это с помощью фигурной инициализации. Поэтому было очень прискорбно, что, хотя мой код никогда не будет пытаться копировать отдельные элементы или саму карту, мне было запрещено использовать контейнер STL.

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

Плюсы:

  • Может хранить std :: atomic внутри любого контейнера STL
  • Не требует / ограничивает использование только определенных методов контейнеров STL.

За или против (желательность этого аспекта зависит от вариантов использования программ):
— есть только один общий атомарный для данного элемента. Таким образом, копирование shared_ptr или контейнера STL все равно даст один общий атомарный элемент. Другими словами, если вы скопируете контейнер STL и измените один из атомарных элементов, соответствующий атомарный элемент другого контейнера также будет отражать новое значение.

В моем случае Pro / Con характеристика была спорно из-за мое использование.

Вот краткий синтаксис для инициализации std :: vector с помощью этого метода:

#include <atomic>
#include <memory>
#include <vector>

std::vector<std::shared_ptr<std::atomic<int> > > vecAtomicInts
{
std::shared_ptr<std::atomic<int> >(new std::atomic<int>(1) ),
std::shared_ptr<std::atomic<int> >(new std::atomic<int>(2) ),
};

// push_back, emplace, etc all supported
vecAtomicInts.push_back(std::shared_ptr<std::atomic<int> >(new std::atomic<int>(3) ) );

// operate atomically on element
vecAtomicInts[1]->exchange(4);

// access random element
int i = *(vecAtomicInts[1]);
0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector