I am intending to declare a vector of atomic variables to be used as counters in a multithreaded programme. Here is what I tried:
#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; }
And this is the annoyingly verbose error message of 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
How do I solve this?
The error disappears when I comment out the line with push_back()
.
edit: I edited the post... For those of you who saw the first post, the error was embarrassingly that I used gcc
instead of g++
:\
Declaration of std::vector The declaration syntax of std::vector is the same as that of std::array, with the difference that we don't need to specify the array length along with the data type as shown below. std::vector<datatype> array_name; For using std::vector, we need to include the <vector> header in our program.
In order to solve this problem, C++ offers atomic variables that are thread-safe. The atomic type is implemented using mutex locks. If one thread acquires the mutex lock, then no other thread can acquire it until it is released by that particular thread.
To add elements to vector, you can use push_back() function. push_back() function adds the element at the end of this vector. Thus, it increases the size of vector by one.
To make these access point's thread safe (for pushing/popping values) you can easily wrap them with your own class and use a mutex along, to secure insertion/deletion operations on the shared queue reference.
As described in this closely related question that was mentioned in the comments, std::atomic<T>
isn't copy-constructible, nor copy-assignable.
Object types that don't have these properties cannot be used as elements of std::vector
.
However, it should be possible to create a wrapper around the std::atomic<T>
element that is copy-constructible and copy-assignable. It will have to use the load()
and store()
member functions of std::atomic<T>
to provide construction and assignment (this is the idea described by the accepted answer to the question mentioned above):
#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; }
EDIT: As pointed out correctly by Bo Persson, the copy operation performed by the wrapper is not atomic. It enables you to copy atomic objects, but the copy itself isn't atomic. This means any concurrent access to the atomics must not make use of the copy operation. This implies that operations on the vector itself (e.g. adding or removing elements) must not be performed concurrently.
Example: If, say, one thread modifies the value stored in one of the atomics while another thread adds new elements to the vector, a vector reallocation may occur and the object the first thread modifies may be copied from one place in the vector to another. In that case there would be a data race between the element access performed by the first thread and the copy operation triggered by the second.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With