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++ :\
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.
To first answer your headline question: you can declare a std::vector<std::atomic<...>> easily, as you have done in your example.
Because of the lack of copy or move constructors for std::atomic<> objects, however, your use of the vector will be restricted as you found out with the compilation error on push_back(). Basically you can't do anything that would invoke either constructor.
This means your vector's size must be fixed at construction, and you should manipulate elements using operator[] or .at(). For your example code, the following works1:
std::vector<std::atomic<int>> v_a(1);
std::atomic<int> a_i(1);
v_a[0] = a_i.load();
If the "fixed size at construction" limitation is too onerous, you can use std::deque instead. This lets you emplace objects, growing the structure dynamically without requiring copy or move constructors, e.g.:
std::deque<std::atomic<int>> d;
d.emplace_back(1);
d.emplace_back(2);
d.pop_back();
There are still some limitations, however. For example, you can pop_back(), but you cannot use the more general erase(). The limitations make sense: an erase() in the middle of the blocks of contiguous storage used by std::deque in general requires the movement of elements, hence requires copy/move constructor or assignment operators to be present.
If you can't live with those limitations, you could create a wrapper class as suggested in other answers but be aware of the underlying implementation: it makes little sense to move a std::atomic<> object once it is being used: it would break any threads concurrently accessing the objects. The only sane use of copy/move constructors is generally in the initial setup of collections of these objects before they are published to other threads.
1 Unless, perhaps, you use Intel's icc compiler, which fails with an internal error while compiling this code.
Looks to me like atomic<T> has no copy constructor. Nor a move constructor, as far as I can tell.
One work around might be to use vector<T>::emplace_back() to construct the atomic in-place in the vector. Alas, I don't have a C++11 compiler on me right now, or I'd go and test it.
As others have properly noted, the cause of the compiler's error is that std::atomic explicitly prohibits the copy constructor.
I had a use case where I wanted the convenience of an STL map (specifically I was using a map of maps in order to achieve a sparse 2-dimensional matrix of atomics so I can do something like int val = my_map[10][5]). In my case there would be only one instance of this map in the program, so it wouldn't be copied, and even better I can initialize the entire thing using braced initialization. So it was very unfortunate that while my code would never attempt copying individual elements or the map itself, I was prevented from using an STL container.
The workaround I ultimately went with is to store the std::atomic inside a std::shared_ptr. This has pros, but possibly a con:
Pros:
Can store std::atomic inside any STL container
Does not require/restrict using only certain methods of STL containers.
Pro or Con (this aspect's desirability depends on the programs' use cases):
- There is only a single shared atomic for a given element. So copying the shared_ptr or the STL container will still yield a single shared atomic for the element. In other words, if you copy the STL container and modify one of the atomic elements, the other container's corresponding atomic element will also reflect the new value.
In my case the Pro/Con characteristic was moot due to my use case.
Here's a brief syntax to initialize a std::vector with this method:
#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]);
Related
What is wrong with this program?
#include <memory>
#include <vector>
int main()
{
std::vector<std::unique_ptr<int>> vec;
int x(1);
std::unique_ptr<int> ptr2x(&x);
vec.push_back(ptr2x); //This tiny command has a vicious error.
return 0;
}
The error:
In file included from c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/mingw32/bits/c++allocator.h:34:0,
from c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/allocator.h:48,
from c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/memory:64,
from main.cpp:6:
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/unique_ptr.h: In member function 'void __gnu_cxx::new_allocator<_Tp>::construct(_Tp*, const _Tp&) [with _Tp = std::unique_ptr<int>, _Tp* = std::unique_ptr<int>*]':
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/stl_vector.h:745:6: instantiated from 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::unique_ptr<int>, _Alloc = std::allocator<std::unique_ptr<int> >, value_type = std::unique_ptr<int>]'
main.cpp:16:21: instantiated from here
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/unique_ptr.h:207:7: error: deleted function 'std::unique_ptr<_Tp, _Tp_Deleter>::unique_ptr(const std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = int, _Tp_Deleter = std::default_delete<int>, std::unique_ptr<_Tp, _Tp_Deleter> = std::unique_ptr<int>]'
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/ext/new_allocator.h:105:9: error: used here
In file included from c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/vector:69:0,
from main.cpp:7:
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/unique_ptr.h: In member function 'void std::vector<_Tp, _Alloc>::_M_insert_aux(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {const std::unique_ptr<int>&}, _Tp = std::unique_ptr<int>, _Alloc = std::allocator<std::unique_ptr<int> >, std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >, typename std::vector<_Tp, _Alloc>::_Base::_Tp_alloc_type::pointer = std::unique_ptr<int>*]':
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/stl_vector.h:749:4: instantiated from 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::unique_ptr<int>, _Alloc = std::allocator<std::unique_ptr<int> >, value_type = std::unique_ptr<int>]'
main.cpp:16:21: instantiated from here
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/unique_ptr.h:207:7: error: deleted function 'std::unique_ptr<_Tp, _Tp_Deleter>::unique_ptr(const std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = int, _Tp_Deleter = std::default_delete<int>, std::unique_ptr<_Tp, _Tp_Deleter> = std::unique_ptr<int>]'
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/vector.tcc:314:4: error: used here
You need to move the unique_ptr:
vec.push_back(std::move(ptr2x));
unique_ptr guarantees that a single unique_ptr container has ownership of the held pointer. This means that you can't make copies of a unique_ptr (because then two unique_ptrs would have ownership), so you can only move it.
Note, however, that your current use of unique_ptr is incorrect. You cannot use it to manage a pointer to a local variable. The lifetime of a local variable is managed automatically: local variables are destroyed when the block ends (e.g., when the function returns, in this case). You need to dynamically allocate the object:
std::unique_ptr<int> ptr(new int(1));
In C++14 we have an even better way to do so:
make_unique<int>(5);
std::unique_ptr has no copy constructor. You create an instance and then ask the std::vector to copy that instance during initialisation.
error: deleted function 'std::unique_ptr<_Tp, _Tp_Deleter>::uniqu
e_ptr(const std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = int, _Tp_D
eleter = std::default_delete<int>, std::unique_ptr<_Tp, _Tp_Deleter> =
std::unique_ptr<int>]'
The class satisfies the requirements of MoveConstructible and
MoveAssignable, but not the requirements of either CopyConstructible
or CopyAssignable.
The following works with the new emplace calls.
std::vector< std::unique_ptr< int > > vec;
vec.emplace_back( new int( 1984 ) );
See using unique_ptr with standard library containers for further reading.
I'm trying to use the ExprTk mathematical expression parser library within a class whose objects are to be stored in a vector of objects, which is a member variable of another class; however, when I try to push_back a new object in the vector I'm getting a lot of "use of deleted function" errors. Here is a simple version of the code that is giving me problems:
#include <exprtk.hpp>
#include <iostream>
#include <string>
#include <vector>
class B {
public:
double x;
exprtk::symbol_table<double> symbol_table;
exprtk::parser<double> parser;
exprtk::expression<double> expr_obj;
B();
};
class A {
public:
std::vector<B> Bvec;
A();
};
A::A() {
Bvec.push_back(B());
};
B::B() {
symbol_table.add_variable("x", x);
expr_obj.register_symbol_table(symbol_table);
parser.compile("x^2",expr_obj);
x = 2.0;
std::cout << expr_obj.value() << std::endl;
}
int main(int argc, char const* argv[]) {
A a_obj;
return 0;
}
I haven't included the header library since it's nearly 40,000 lines, but it can be found here: http://www.partow.net/programming/exprtk/.
Here is the error message
In file included from /usr/include/x86_64-linux-gnu/c++/7/bits/c++allocator.h:33:0,
from /usr/include/c++/7/bits/allocator.h:46,
from /usr/include/c++/7/string:41,
from /usr/include/c++/7/bits/locale_classes.h:40,
from /usr/include/c++/7/bits/ios_base.h:41,
from /usr/include/c++/7/ios:42,
from /usr/include/c++/7/ostream:38,
from /usr/include/c++/7/iostream:39,
from src/main.cpp:1:
/usr/include/c++/7/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = B; _Args = {B}; _Tp = B]’:
/usr/include/c++/7/bits/alloc_traits.h:475:4: required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = B; _Args = {B}; _Tp = B; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<B>]’
/usr/include/c++/7/bits/vector.tcc:100:30: required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {B}; _Tp = B; _Alloc = std::allocator<B>]’
/usr/include/c++/7/bits/stl_vector.h:954:21: required from ‘void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = B; _Alloc = std::allocator<B>; std::vector<_Tp, _Alloc>::value_type = B]’
src/main.cpp:25:21: required from here
/usr/include/c++/7/ext/new_allocator.h:136:4: error: use of deleted function ‘B::B(B&&)’
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.cpp:6:7: note: ‘B::B(B&&)’ is implicitly deleted because the default definition would be ill-formed:
class B {
^
src/main.cpp:6:7: error: ‘exprtk::parser<T>::parser(const exprtk::parser<T>&) [with T = double]’ is private within this context
In file included from src/main.cpp:3:0:
ext_libs/exprtk/exprtk.hpp:35289:7: note: declared private here
parser(const parser<T>&);
^~~~~~
In file included from /usr/include/c++/7/bits/stl_tempbuf.h:60:0,
from /usr/include/c++/7/bits/stl_algo.h:62,
from /usr/include/c++/7/algorithm:62,
from ext_libs/exprtk/exprtk.hpp:37,
from src/main.cpp:3:
/usr/include/c++/7/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = B; _Args = {B}]’:
/usr/include/c++/7/bits/stl_uninitialized.h:83:18: required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<B*>; _ForwardIterator = B*; bool _TrivialValueTypes = false]’
/usr/include/c++/7/bits/stl_uninitialized.h:134:15: required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<B*>; _ForwardIterator = B*]’
/usr/include/c++/7/bits/stl_uninitialized.h:289:37: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::move_iterator<B*>; _ForwardIterator = B*; _Tp = B]’
/usr/include/c++/7/bits/stl_uninitialized.h:311:2: required from ‘_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = B*; _ForwardIterator = B*; _Allocator = std::allocator<B>]’
/usr/include/c++/7/bits/vector.tcc:426:6: required from ‘void std::vector<_Tp, _Alloc>::_M_realloc_insert(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {B}; _Tp = B; _Alloc = std::allocator<B>; std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<B*, std::vector<B> >; typename std::_Vector_base<_Tp, _Alloc>::pointer = B*]’
/usr/include/c++/7/bits/vector.tcc:105:21: required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {B}; _Tp = B; _Alloc = std::allocator<B>]’
/usr/include/c++/7/bits/stl_vector.h:954:21: required from ‘void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = B; _Alloc = std::allocator<B>; std::vector<_Tp, _Alloc>::value_type = B]’
src/main.cpp:25:21: required from here
/usr/include/c++/7/bits/stl_construct.h:75:7: error: use of deleted function ‘B::B(B&&)’
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Makefile:29: recipe for target 'obj/src/main.o' failed
make: *** [obj/src/main.o] Error 1
I'm pretty sure the issue has something to do with the push_back function and how the object is being copied; however, that's a bit over my head and knowledge of c++ (which is rather basic).
Any help would be appreciated. Thanks!
Looking at the ExprTk documentation (readme.txt), specifically Section 10.3 we have the following note:
Note: The exprtk::parser is a non-copyable and non-thread safe
component, and should only be shared via either a reference, a shared
pointer or a std::ref mechanism, and considerations relating to
synchronisation taken into account where appropriate. The parser
represents an object factory, specifically a factory of expressions,
and generally should not be instantiated solely on a per expression
compilation basis.
Section 10.3
parser cannot be copied, and based on the signature of how copying was prevented, the copy constructor was made private, it's unlikely that it can be moved either (private copy constructers were what you used before the disabling of a special member function with the delete keyword was added to the language in C++11, the same time move semantics were added). You cannot have a exprtk::parser instance as a member of an object you want to copy (unless you're going to get really weird in custom special member functions and NOT copy the exprtk::parser).
This is all enforcement of you not wanting to have multiple instance of the same Parser floating around. You will have to use a reference, most likely a smart pointer because references are a <expletive deleted> to copy assign, to a single instance instead.
But this raises the question of whether you need to keep parser around as a member at all. What about something like this:
class B {
public:
double x; // Not sure we need even this.
double result;
B();
};
B::B():
{
// parser is handled with local variables.
exprtk::symbol_table<double> symbol_table;
exprtk::parser<double> parser;
exprtk::expression<double> expr_obj;
symbol_table.add_variable("x", x);
expr_obj.register_symbol_table(symbol_table);
parser.compile("x^2",expr_obj);
x = 2.0;
result = expr_obj.value(); // store instead of printing
} // parser and friends are no longer needed and discarded.
#include <vector>
#include <memory>
#include <iostream>
class A {
std::vector<std::unique_ptr<int>> vec;
public:
virtual ~A() = 0;
};
A::~A() {}
class B : public A {
public:
B() {}
};
int main () {
B b = B();
return 0;
}
Above is a minimal reproducible example. It does not compile, and the following error message is given:
me:~ $ g++ main2.cc
In file included from /usr/include/c++/7/vector:62:0,
from main2.cc:1:
/usr/include/c++/7/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = std::unique_ptr<int>; _Args = {const std::unique_ptr<int, std::default_delete<int> >&}]’:
/usr/include/c++/7/bits/stl_uninitialized.h:83:18: required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _ForwardIterator = std::unique_ptr<int>*; bool _TrivialValueTypes = false]’
/usr/include/c++/7/bits/stl_uninitialized.h:134:15: required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _ForwardIterator = std::unique_ptr<int>*]’
/usr/include/c++/7/bits/stl_uninitialized.h:289:37: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _ForwardIterator = std::unique_ptr<int>*; _Tp = std::unique_ptr<int>]’
/usr/include/c++/7/bits/stl_vector.h:331:31: required from ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = std::unique_ptr<int>; _Alloc = std::allocator<std::unique_ptr<int> >]’
main2.cc:5:7: required from here
/usr/include/c++/7/bits/stl_construct.h:75:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/7/memory:80:0,
from main2.cc:2:
/usr/include/c++/7/bits/unique_ptr.h:388:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^~~~~~~~~~
What it all seems like is that the copy constructor for the unique_ptr is being called somewhere.
But I have no idea why it would be called at all. When a vector is initialized, it has size 0, right? Under that assumption, no unique_ptr should be constructed.
What is the source of my compilation error?
B b = B() is a copy-initialisation and, until C++17, required that the copy be valid (even if it ended up being elided).
Even though the size of the vector in that copy is zero, that is not something that can be statically known by the innards of the copy machinery during compile-time. So, the code to make it work must be "known" during compilation, so that it is available at runtime if needed.
You probably meant just a normal declaration: B b;.
The original approach would actually have been enough if your class were trivially-moveable (because a unique_ptr can be moved), but by giving it a user-declared destructor you pessimised it. Unless you actually have a strong reason to want this class to be polymorphic, don't do that. Use = default or just omit the destructor entirely; it's not doing anything useful.
Below code compiles fine with clang but not gcc , any explanation is this a bug in gcc?
Its just a class which contains a vector of unique_ptr and std::function as member and when I create vector of this class , I cant say reserve or resize on this. push_back works fine with std::move, while this only happens with gcc and not clang.
#include <algorithm>
#include <memory>
#include <utility>
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
class ABC
{
public:
ABC()
{}
private:
std::vector<std::unique_ptr<int>> up;
std::function<void (int*)> func;
};
int main()
{
ABC a;
std::vector<ABC> vec;
vec.reserve(1);
}
Error message looks like below for gcc
In file included from /opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_tempbuf.h:60:0,
from /opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_algo.h:62,
from /opt/wandbox/gcc-7.1.0/include/c++/7.1.0/algorithm:62,
from prog.cc:1:
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...) [with _T1 = std::unique_ptr<int>; _Args = {const std::unique_ptr<int, std::default_delete<int> >&}]':
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_uninitialized.h:83:18: required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _ForwardIterator = std::unique_ptr<int>*; bool _TrivialValueTypes = false]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_uninitialized.h:134:15: required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _ForwardIterator = std::unique_ptr<int>*]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_uninitialized.h:289:37: required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _ForwardIterator = std::unique_ptr<int>*; _Tp = std::unique_ptr<int>]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_vector.h:331:31: required from 'std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = std::unique_ptr<int>; _Alloc = std::allocator<std::unique_ptr<int> >]'
prog.cc:10:7: required from 'void std::_Construct(_T1*, _Args&& ...) [with _T1 = ABC; _Args = {const ABC&}]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_uninitialized.h:83:18: required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const ABC*; _ForwardIterator = ABC*; bool _TrivialValueTypes = false]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_uninitialized.h:134:15: required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const ABC*; _ForwardIterator = ABC*]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_uninitialized.h:289:37: required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = const ABC*; _ForwardIterator = ABC*; _Tp = ABC]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_vector.h:1263:35: required from 'std::vector<_Tp, _Alloc>::pointer std::vector<_Tp, _Alloc>::_M_allocate_and_copy(std::vector<_Tp, _Alloc>::size_type, _ForwardIterator, _ForwardIterator) [with _ForwardIterator = const ABC*; _Tp = ABC; _Alloc = std::allocator<ABC>; std::vector<_Tp, _Alloc>::pointer = ABC*; std::vector<_Tp, _Alloc>::size_type = long unsigned int]'
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/vector.tcc:73:40: required from 'void std::vector<_Tp, _Alloc>::reserve(std::vector<_Tp, _Alloc>::size_type) [with _Tp = ABC; _Alloc = std::allocator<ABC>; std::vector<_Tp, _Alloc>::size_type = long unsigned int]'
prog.cc:24:18: required from here
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/stl_construct.h:75:7: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]'
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /opt/wandbox/gcc-7.1.0/include/c++/7.1.0/memory:80:0,
from prog.cc:2:
/opt/wandbox/gcc-7.1.0/include/c++/7.1.0/bits/unique_ptr.h:388:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^~~~~~~~~~
This happens because std::function's move ctor is not noexcept, but a std::vector can only use the move ctor if it's noexcept (strong exception guarantee).
The issue is that std::unique_ptr is (obviously) non-copyable, so that makes ABC noncopyable as a whole.
To make ABC noexcept-movable implicitly, it'd need every one of its members to be noexcept-movable as well.
If you remove the std::function, that's what happens: the .resize() doesn't need to copy A.up (a std::vector's move operator is noexcept), so the std::unique_ptrs (inside up) can just be moved, and everything works fine.
See this coliru: if you comment the noexcept, vec.reserve() will need to copy everything, and the issue comes back.
Adding ABC(ABC&&) = default fixes the issue because user-declared (though defaulted) move ctor inhibits the generation of copy constructor (see here).
We can also go the opposite way and manually delete A's copy constructor in the coliru (and keep the move ctor not noexcept): here.
To fix the issue, you could store the std::function in a std::unique_ptr, which has a nothrow move ctor. See here
I think that GCC is being picky about constructors; it blocks the generation of the move-ctor1. It stops complaining after you provide them explicitely:
class ABC
{
public:
ABC() = default; // <--
ABC(ABC&&) = default; // <--
private:
std::vector<std::unique_ptr<int>> up;
std::function<void (int*)> func;
};
1 I think (again, I'm not sure), that his happens because the default copy-ctor of ABC is defined as noexcept and is as such preferred over the default move-ctor (it's still ill-formed because we're dealing with non-copyable members here). Trying to generate a defaulted noexcept move-ctor result in:
main.cpp:14:4: note: 'ABC::ABC(ABC&&) noexcept' is implicitly deleted because its exception-specification does not match the implicit exception-specification ''
ABC(ABC&&) noexcept = default;
^~~
As such, forcing the generation of no-noexcept move-ctor allows it to be picked. Why Clang doesn't have a problem with that - I don't know.
One explanation for the above is hinted by the fact that removing std::function allows the defaulted noexcept move-ctor to be generated. std::function doesn't have a noexcept move-ctor (which sucks tremendously), so the whole class falls back to the copy-ctor. Since unique_ptr doesn't have one, the whole thing breaks.
GCC accepted the lack of noexcept on std::function's move ctor as bug
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81017
// -- C++ --
/** 2227 * #brief %Function move constructor. 2228 * #param
__x A %function object rvalue with identical call signature. 2229 * 2230
* The newly-created %function contains the target of #a __x 2231 * (if
it has one). 2232 */ 2233
function(function&& __x) : _Function_base() { __x.swap(*this); }
What is wrong with this program?
#include <memory>
#include <vector>
int main()
{
std::vector<std::unique_ptr<int>> vec;
int x(1);
std::unique_ptr<int> ptr2x(&x);
vec.push_back(ptr2x); //This tiny command has a vicious error.
return 0;
}
The error:
In file included from c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/mingw32/bits/c++allocator.h:34:0,
from c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/allocator.h:48,
from c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/memory:64,
from main.cpp:6:
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/unique_ptr.h: In member function 'void __gnu_cxx::new_allocator<_Tp>::construct(_Tp*, const _Tp&) [with _Tp = std::unique_ptr<int>, _Tp* = std::unique_ptr<int>*]':
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/stl_vector.h:745:6: instantiated from 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::unique_ptr<int>, _Alloc = std::allocator<std::unique_ptr<int> >, value_type = std::unique_ptr<int>]'
main.cpp:16:21: instantiated from here
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/unique_ptr.h:207:7: error: deleted function 'std::unique_ptr<_Tp, _Tp_Deleter>::unique_ptr(const std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = int, _Tp_Deleter = std::default_delete<int>, std::unique_ptr<_Tp, _Tp_Deleter> = std::unique_ptr<int>]'
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/ext/new_allocator.h:105:9: error: used here
In file included from c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/vector:69:0,
from main.cpp:7:
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/unique_ptr.h: In member function 'void std::vector<_Tp, _Alloc>::_M_insert_aux(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {const std::unique_ptr<int>&}, _Tp = std::unique_ptr<int>, _Alloc = std::allocator<std::unique_ptr<int> >, std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >, typename std::vector<_Tp, _Alloc>::_Base::_Tp_alloc_type::pointer = std::unique_ptr<int>*]':
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/stl_vector.h:749:4: instantiated from 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::unique_ptr<int>, _Alloc = std::allocator<std::unique_ptr<int> >, value_type = std::unique_ptr<int>]'
main.cpp:16:21: instantiated from here
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/unique_ptr.h:207:7: error: deleted function 'std::unique_ptr<_Tp, _Tp_Deleter>::unique_ptr(const std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = int, _Tp_Deleter = std::default_delete<int>, std::unique_ptr<_Tp, _Tp_Deleter> = std::unique_ptr<int>]'
c:\mingw\bin\../lib/gcc/mingw32/4.5.0/include/c++/bits/vector.tcc:314:4: error: used here
You need to move the unique_ptr:
vec.push_back(std::move(ptr2x));
unique_ptr guarantees that a single unique_ptr container has ownership of the held pointer. This means that you can't make copies of a unique_ptr (because then two unique_ptrs would have ownership), so you can only move it.
Note, however, that your current use of unique_ptr is incorrect. You cannot use it to manage a pointer to a local variable. The lifetime of a local variable is managed automatically: local variables are destroyed when the block ends (e.g., when the function returns, in this case). You need to dynamically allocate the object:
std::unique_ptr<int> ptr(new int(1));
In C++14 we have an even better way to do so:
make_unique<int>(5);
std::unique_ptr has no copy constructor. You create an instance and then ask the std::vector to copy that instance during initialisation.
error: deleted function 'std::unique_ptr<_Tp, _Tp_Deleter>::uniqu
e_ptr(const std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = int, _Tp_D
eleter = std::default_delete<int>, std::unique_ptr<_Tp, _Tp_Deleter> =
std::unique_ptr<int>]'
The class satisfies the requirements of MoveConstructible and
MoveAssignable, but not the requirements of either CopyConstructible
or CopyAssignable.
The following works with the new emplace calls.
std::vector< std::unique_ptr< int > > vec;
vec.emplace_back( new int( 1984 ) );
See using unique_ptr with standard library containers for further reading.