How to fix vector of objects that have unique_ptr - c++

I have following code that has vector of a class that has some member declared as unique_ptr.
struct Container
{
struct Nested{
std::unique_ptr<Container> node;
Nested(std::unique_ptr<Container> t) : node(std::move(t)) {}
Nested(const Nested& t) { node = std::move(t.node); };
};
std::vector<Nested> edges;
};
typedef std::unique_ptr<Container> UCont;
typedef Container::Nested Nested;
int main()
{
std::unique_ptr<Container> object = UCont(new Container{{
Nested(UCont(new Container{{}})),
Nested(UCont(new Container{{}})),
Nested(UCont(new Container{{}}))
}});
}
Now compiling this is giving the following error:
..\00.UniquePtrVector.cpp: In copy constructor 'Container::Nested::Nested(const Container::Nested&)':
..\00.UniquePtrVector.cpp:20:35: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Container; _Dp = std::default_delete<Container>]'
Nested(const Nested& t) { node = std::move(t.node); };
^
In file included from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\locale_conv.h:41:0,
from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\locale:43,
from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\iomanip:43,
from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\mingw32\bits\stdc++.h:71,
from ..\00.UniquePtrVector.cpp:10:
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\unique_ptr.h:357:19: note: declared here
unique_ptr& operator=(const unique_ptr&) = delete;
I am not sure how to fix this error. I guess removing the copy constructor is not an option either. Any help?
EDIT:
Changing to
Nested(std::unique_ptr<Container>&& t) : node(std::move(t)) {}
Nested(Nested&& t) : node(std::move(t.node)) {}
Nested(const Nested& t) =delete;
is also giving error:
In file included from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_tempbuf.h:60:0,
from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_algo.h:62,
from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\algorithm:62,
from c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\mingw32\bits\stdc++.h:64,
from ..\00.UniquePtrVector.cpp:10:
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...) [with _T1 = Container::Nested; _Args = {const Container::Nested&}]':
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_uninitialized.h:75:18: required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const Container::Nested*; _ForwardIterator = Container::Nested*; bool _TrivialValueTypes = false]'
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_uninitialized.h:126:15: required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const Container::Nested*; _ForwardIterator = Container::Nested*]'
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_uninitialized.h:281:37: required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = const Container::Nested*; _ForwardIterator = Container::Nested*; _Tp = Container::Nested]'
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_vector.h:1290:33: required from 'void std::vector<_Tp, _Alloc>::_M_range_initialize(_ForwardIterator, _ForwardIterator, std::forward_iterator_tag) [with _ForwardIterator = const Container::Nested*; _Tp = Container::Nested; _Alloc = std::allocator<Container::Nested>]'
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_vector.h:377:21: required from 'std::vector<_Tp, _Alloc>::vector(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = Container::Nested; _Alloc = std::allocator<Container::Nested>; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<Container::Nested>]'
..\00.UniquePtrVector.cpp:36:11: required from here
c:\mingw\lib\gcc\mingw32\5.3.0\include\c++\bits\stl_construct.h:75:7: error: use of deleted function 'Container::Nested::Nested(const Container::Nested&)'
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^
..\00.UniquePtrVector.cpp:20:11: note: declared here
Nested(const Nested& t) =delete;
^

When you move t.node in your copy constructor, t.node needs to change. But t is const, so the move is invalid. unique_ptr cannot be copy constructed, thus struct Nested cannot be copy constructed either.
To make it work, you'll need to supply a move constructor and delete the copy constructor. Something like this:
struct Nested{
std::unique_ptr<Container> node;
Nested(std::unique_ptr<Container>&& t) : node(std::move(t)) {}
Nested(Nested&& t) : node(std::move(t.node)) {}
Nested(const Nested& t) =delete;
};

tl;dr : Consider using the rule of zero
I would be wary of explicitly specifying all those constructors in your class. The fact that unique_ptr's can't be copied will get you in all sorts of trouble when you do things the way you tried to. Here's what happens when, instead, you don't specify any constructors:
#include <vector>
#include <memory>
struct Container
{
struct Nested{
std::unique_ptr<Container> node;
//
// Nested(std::unique_ptr<Container> t) : node(std::move(t)) {}
// Nested(const Nested& t) { node = std::move(t.node); };
};
std::vector<Nested> edges;
};
typedef std::unique_ptr<Container> UCont;
typedef Container::Nested Nested;
int main()
{
auto c1 = new Container{};
auto c2 = new Container{};
auto c3 = new Container{};
std::unique_ptr<Container> u1 {c1};
std::unique_ptr<Container> u2 {c2};
std::unique_ptr<Container> u3 {c3};
Nested n1 {std::move(u1)};
Nested n2 {std::move(u2)};
Nested n3 {std::move(u3)};
auto v = std::vector<Nested>{3};
v.push_back(std::move(n1));
v.push_back(std::move(n2));
v.push_back(std::move(n3));
auto c5 = new Container { std::move(v) };
std::unique_ptr<Container> object = UCont(std::move(c5));
}
I've broken everything down to shorter statements for clarity (mostly).

The root cause of the error I was getting is due to use of unique_ptr inside the object that is used in intializer list. Similar to vector of simple unique_ptr (as in here Initializing container of unique_ptrs from initializer list fails with GCC 4.7 ) an object that also has unique_ptr in its data model can also NOT be used in initializer list.
Similar to the other link, cause is same; initializer list always performs copies and unique_ptr cannot be copied. So we have to use emplace_back/push_back.
So even with constructor in place the following solution works
struct Container
{
struct Nested{
std::unique_ptr<Container> node;
Nested(): node(nullptr) {}
Nested(std::unique_ptr<Container> t) : node(std::move(t)) {}
};
std::vector<Nested> edges;
};
typedef std::unique_ptr<Container> UCont;
typedef Container::Nested Nested;
int main()
{
auto v = std::vector<Nested>{3};
v.push_back(std::move(Nested(std::move(std::unique_ptr<Container>(new Container{})))));
v.push_back(std::move(Nested(std::move(std::unique_ptr<Container>(new Container{})))));
v.push_back(std::move(Nested(std::move(std::unique_ptr<Container>(new Container{})))));
std::unique_ptr<Container> object = UCont(new Container { std::move(v) });
}

Related

How do I move a vector of objects that contain a unique_ptr as a member?

I have a function that returns an std::vector of a class that contains a std::unique_ptr as member. I need to store this vector object on the heap so I can pass it through a C-style DLL interface.
See the following code sample:
#include <iostream>
#include <cstdlib>
#include <memory>
#include <vector>
// I have control over the following two classes
class SomeBigClassWithManyMembers { };
class MyClass
{
std::unique_ptr<SomeBigClassWithManyMembers> up;
public:
static const std::vector<MyClass> GetMany()
{
// imagine this does lots of work
return std::vector<MyClass>(50);
}
// following code is suggested in https://stackoverflow.com/questions/31430533/move-assignable-class-containing-vectorunique-ptrt but doesn't help
/*
MyClass() { }
MyClass(MyClass&& other): up{std::move(other.up)} { }
MyClass& operator=(MyClass&& other)
{
up = std::move(other.up);
return *this;
}
*/
};
// Imagine that I can't change this C-style code - it's a fixed interface provided by someone else
struct NastyCStyleStruct
{
void* v;
};
void NastyCStyleInterface(NastyCStyleStruct s) { printf("%u", (unsigned int)((std::vector<MyClass>*)s.v)->size()); }
int main()
{
NastyCStyleStruct s;
s.v = new std::vector<MyClass>(std::move(MyClass::GetMany()));
NastyCStyleInterface(s);
return 0;
}
Note that in my actual code, the vector needs to outlive the function in which it is created (because this is being done in a DLL), so writing
auto vec = MyClass::GetMany();
s.v = &vec;
would not suffice. The vector must be stored on the heap.
The trouble here is that the code seems to try to use the (non-existent) copy constructor of MyClass. I can't understand why the copy constructor is being invoked, because I am explicitly asking for move semantics with std::move. Not even initialising s.v to a fresh new std::vector of the appropriate size and calling the three-argument version of std::move works.
Errors from g++:
In file included from /usr/include/c++/7/memory:64:0,
from stackq.cpp:4:
/usr/include/c++/7/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = MyClass; _Args = {const MyClass&}]’:
/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 MyClass*, std::vector<MyClass> >; _ForwardIterator = MyClass*; 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 MyClass*, std::vector<MyClass> >; _ForwardIterator = MyClass*]’
/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 MyClass*, std::vector<MyClass> >; _ForwardIterator = MyClass*; _Tp = MyClass]’
/usr/include/c++/7/bits/stl_vector.h:331:31: required from ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = MyClass; _Alloc = std::allocator<MyClass>]’
stackq.cpp:43:65: required from here
/usr/include/c++/7/bits/stl_construct.h:75:7: error: use of deleted function ‘MyClass::MyClass(const MyClass&)’
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
stackq.cpp:10:7: note: ‘MyClass::MyClass(const MyClass&)’ is implicitly deleted because the default definition would be ill-formed:
class MyClass
^~~~~~~
stackq.cpp:10:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = SomeBigClassWithManyMembers; _Dp = std::default_delete<SomeBigClassWithManyMembers>]’
In file included from /usr/include/c++/7/memory:80:0,
from stackq.cpp:4:
/usr/include/c++/7/bits/unique_ptr.h:388:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^~~~~~~~~~
How can I repair this code so that the vector object itself is stored on the heap?
The problem here is that GetMany is defined to return a const std::vector. Even though it's returning by value, so there's no risk of unwanted side-effects if you mutate the result, the compiler still enforces the type (auto just copied the exact return type of the function after all), making it impossible to move from it. So instead of cheaply copying a few pointer/size_t-sized values (to the vector contents, size and capacity), it had to do a full copy construction, including copy constructing all the values stored in the original vector. That fails due to the unique_ptr member in the stored instances.
Simply dropping the const from the return type should allow std::move/vector's move constructor to do their job, allowing the ultra-cheap extraction of the contents of the vector returned by GetMany with no copies (or even moves) of any instance of MyClass.

std::vector doesn't call move constructor with objects with noexcept move constructs

I've been struggeling a lot lately with move and copy constructors and can't seem to find the awnser on my own.
The structure is fairly simple. A class OWUP which holds a std::unique_ptr to an object (in this example an int), a class One which is a simple wrapper around std::vector of OWUP's and class Two which is a simple wrapper std::vector of One's.
#include <memory>
#include <vector>
class OWUP {
public:
OWUP()
: data(nullptr)
{ }
OWUP(const OWUP &) = delete;
OWUP &operator=(const OWUP &) = delete;
OWUP(OWUP &&) noexcept = default;
OWUP &operator=(OWUP &&) noexcept = default;
std::unique_ptr<int> data;
};
class One {
public:
One(std::size_t numof_datas)
: datas(numof_datas)
{ }
One(const One &) = delete;
One &operator=(const One &) = delete;
One(One &&) noexcept = default;
One &operator=(One &&) noexcept = default;
std::vector<OWUP> datas;
};
class Two {
public:
Two(std::size_t numof_ones, std::size_t num_of_datas)
: ones(numof_ones, One(num_of_datas))
{ }
Two(const Two &) = delete;
Two &operator=(const Two &) = delete;
Two(Two &&) noexcept = default;
Two &operator=(Two &&) noexcept = default;
std::vector<One> ones;
};
The code gets the following error code with g++ -std=c++14 example.cpp
In file included from /usr/include/c++/7.3.1/memory:64:0,
from example.cpp:1:
/usr/include/c++/7.3.1/bits/stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...) [with _T1 = One; _Args = {const One&}]':
/usr/include/c++/7.3.1/bits/stl_uninitialized.h:210:18: required from 'static _ForwardIterator std::__uninitialized_fill_n<_TrivialValueType>::__uninit_fill_n(_ForwardIterator, _Size, const _Tp&) [with _ForwardIterator = One*; _Size = long unsigned int; _Tp = One; bool _TrivialValueType = false]'
/usr/include/c++/7.3.1/bits/stl_uninitialized.h:255:17: required from '_ForwardIterator std::uninitialized_fill_n(_ForwardIterator, _Size, const _Tp&) [with _ForwardIterator = One*; _Size = long unsigned int; _Tp = One]'
/usr/include/c++/7.3.1/bits/stl_uninitialized.h:366:39: required from '_ForwardIterator std::__uninitialized_fill_n_a(_ForwardIterator, _Size, const _Tp&, std::allocator<_Tp2>&) [with _ForwardIterator = One*; _Size = long unsigned int; _Tp = One; _Tp2 = One]'
/usr/include/c++/7.3.1/bits/stl_vector.h:1337:33: required from 'void std::vector<_Tp, _Alloc>::_M_fill_initialize(std::vector<_Tp, _Alloc>::size_type, const value_type&) [with _Tp = One; _Alloc = std::allocator<One>; std::vector<_Tp, _Alloc>::size_type = long unsigned int; std::vector<_Tp, _Alloc>::value_type = One]'
/usr/include/c++/7.3.1/bits/stl_vector.h:298:27: required from 'std::vector<_Tp, _Alloc>::vector(std::vector<_Tp, _Alloc>::size_type, const value_type&, const allocator_type&) [with _Tp = One; _Alloc = std::allocator<One>; std::vector<_Tp, _Alloc>::size_type = long unsigned int; std::vector<_Tp, _Alloc>::value_type = One; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<One>]'
example.cpp:40:39: required from here
/usr/include/c++/7.3.1/bits/stl_construct.h:75:7: error: use of deleted function 'One::One(const One&)'
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
example.cpp:29:3: note: declared here
One(const One &) = delete;
^~~
I've tried my best to force it to use move constructors. I also tried to create my own move constructors with std::move, but that resulted int the same compile error. I'm aware that std::vector tests for move_if_noexcept(), but for can't find out why it doesn't. What is it that i'm doing wrong?
Because the problem is here:
Two(std::size_t numof_ones, std::size_t num_of_datas)
: ones(numof_ones, One(num_of_datas))
{ }
You can't move from One you create here, you really need to copy from it numof_ones times.
Unfortunately, there is no easy way of constructing a std::vector<One> of size numof_ones without One having a default constructor or a copy constructor. The constructor you chose takes numof_ones copies of the single value you give it into each element of the vector.
The easiest solution is probably to not initialize ones in the constructor initializer list but instead initialize it in the body of the constructor:
Two(std::size_t numof_ones, std::size_t num_of_datas) {
ones.reserve(numof_ones);
for (size_t i = 0; i != numof_ones; ++i) {
ones.emplace_back(num_of_datas);
}
}
(Technically you could probably use the std::vector constructor that takes two iterators and provide your own custom iterator but it is probably not worth the effort).

STL vector and unique_ptr with custom deleter classes

I have some c++ code that compiles with clang 3.2-7 and gcc 4.8.1 but not with gcc 4.6.3. I'm compiling with c++0x.
I'm implementing a 'ResourcePool' that manages a fixed set of resources. It allocates resources out by providing a unique_ptr with a custom deleter class; when the object requesting the resource drops out of scope, the deleter returns the resource to a pool.
The deleter class looks like so:
template <typename T>
class ResourcePoolManager {
public:
ResourcePoolManager(ResourcePool<T> & pool)
: pool(pool)
{
}
~ResourcePoolManager() {};
void operator()(T* releasedResource) const {
pool.releaseResource(releasedResource);
}
private:
ResourcePool<T> & pool;
};
The pool itself looks like (edited unrelated methods out):
template <typename T>
class ResourcePool {
public:
ResourcePool()
: manager(*this)
{
}
std::unique_ptr<T, ResourcePoolManager<T>> requestResource() {
if(availableResources.size() == 0) {
return std::unique_ptr<T, ResourcePoolManager<T>>(NULL, manager);
} else {
T * resource = availableResources.front();
availableResources.pop_front();
return std::unique_ptr<T, ResourcePoolManager<T>>(resource, manager);
}
}
private:
friend class ResourcePoolManager<T>;
void releaseResource(T * releasedResource) {
availableResources.push_back(releasedResource);
}
ResourcePoolManager<T> manager;
std::deque<T *> availableResources;
std::deque<T *> allResources;
};
The problem occurs when I try to pull resources out of the pool and store them in a std::vector; where OpenCLDevice is the resource in question:
ResourcePool<OpenCLDevice> computeDevicePool;
std::unique_ptr<OpenCLDevice, ResourcePoolManager<OpenCLDevice>> devicePtr = computeDevicePool.requestResource();
std::vector<std::unique_ptr<OpenCLDevice, ResourcePoolManager<OpenCLDevice>>> gpus;
gpus.push_back(std::move(devicePtr));
As mentioned up top, clang and a newer version are gcc are fine with this. However gcc 4.6.3 is coming out with:
/usr/include/c++/4.6/bits/unique_ptr.h: In member function 'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = OpenCLDevice, _Dp = ResourcePoolManager<OpenCLDevice>, std::unique_ptr<_Tp, _Dp> = std::unique_ptr<OpenCLDevice, ResourcePoolManager<OpenCLDevice> >]':
/usr/include/c++/4.6/bits/vector.tcc:319:4: instantiated from 'void std::vector<_Tp, _Alloc>::_M_insert_aux(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {std::unique_ptr<OpenCLDevice, ResourcePoolManager<OpenCLDevice> >}, _Tp = std::unique_ptr<OpenCLDevice, ResourcePoolManager<OpenCLDevice> >, _Alloc = std::allocator<std::unique_ptr<OpenCLDevice, ResourcePoolManager<OpenCLDevice> > >, std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<std::unique_ptr<OpenCLDevice, ResourcePoolManager<OpenCLDevice> >*, std::vector<std::unique_ptr<OpenCLDevice, ResourcePoolManager<OpenCLDevice> > > >, typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer = std::unique_ptr<OpenCLDevice, ResourcePoolManager<OpenCLDevice> >*]'
/usr/include/c++/4.6/bits/vector.tcc:102:4: instantiated from 'void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {std::unique_ptr<OpenCLDevice, ResourcePoolManager<OpenCLDevice> >}, _Tp = std::unique_ptr<OpenCLDevice, ResourcePoolManager<OpenCLDevice> >, _Alloc = std::allocator<std::unique_ptr<OpenCLDevice, ResourcePoolManager<OpenCLDevice> > >]'
/usr/include/c++/4.6/bits/stl_vector.h:840:9: instantiated from 'void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = std::unique_ptr<OpenCLDevice, ResourcePoolManager<OpenCLDevice> >, _Alloc = std::allocator<std::unique_ptr<OpenCLDevice, ResourcePoolManager<OpenCLDevice> > >, std::vector<_Tp, _Alloc>::value_type = std::unique_ptr<OpenCLDevice, ResourcePoolManager<OpenCLDevice> >]'
trunk/src/dpa/distinguishers/GenericOpenCLDPA.cpp:109:39: instantiated from here
/usr/include/c++/4.6/bits/unique_ptr.h:176:2: error: use of deleted function 'ResourcePoolManager<OpenCLDevice>& ResourcePoolManager<OpenCLDevice>::operator=(const ResourcePoolManager<OpenCLDevice>&)'
ResourcePool.hpp:30:7 error: 'ResourcePoolManager<OpenCLDevice> & ResourcePoolManager<OpenCLDevice>::operator=(const ResourcePoolManager<OpenCLDevice>&)'
is implicitly deleted because the default definition would be ill-formed:
ResourcePool.hpp:30:7: error: non-static reference member 'ResourcePool<OpenCLDevice>& ResourcePoolManager<OpenCLDevice>::pool', can't use default assignment operator
The compiler stops at the final line where the pointer is moved into the gpus vector, indicating to me that something is being copied when it shouldn't be. Line 30 is the definition of the ResourcePoolManager class class ResourcePoolManager {.
Am I doing something incorrectly with the management of the custom deleter where the newer compilers are letting me get away with things?
Looks like a bug in the libstdc++ 4.6 unique_ptr that expects the deleter type to be copyable. Changing ResourcePoolManager::pool to a pointer instead of a reference is an effective workaround.
More specifically, this program reproduces the bug:
#include <memory>
#include <vector>
struct deleter {
deleter() : c(*"") {}
void operator () (int *) const {}
deleter(deleter&&) = default;
deleter& operator = (deleter&&) = default;
const char& c;
};
int main() {
std::vector<std::unique_ptr<int, deleter>> vec;
vec.push_back(std::unique_ptr<int, deleter>{});
}
simply declaring the vector is fine, its the instantiation of push_back that fails.
Clarification: the issue seems to be that the 4.6 implementation of vector is using = to move assign elements when it resizes, whereas the 4.8 implementation is exclusively using move construction. A class with a reference member - such as your deleter - is move constructible, but not move (or copy, for that matter) assignable. You can see the same error message in 4.8 if you assign to the vector:
int main() {
std::vector<std::unique_ptr<int, deleter>> vec;
vec.push_back(std::unique_ptr<int, deleter>{new int(42)});
vec[0] = std::unique_ptr<int, deleter>{new int(42)};
}
std::vector::push_back is specified to require only that T be MoveInsertable, so I believe this is certainly a bug in libstdc++ 4.6.

in-placement constructor std::vector

Can I create vector that contains elements that are noncopyable and don't have a default constructor in C++11?
example:
#include <iostream>
#include <string>
#include <vector>
struct value {
value() = delete;
~value() = default;
value(value const&) = delete;
value& operator =(value const&) = delete;
explicit value(int i) : i_(i) {}
private:
int i_;
};
int main() {
std::vector<value> v;
v.reserve(10);
for (unsigned i = 0; i < 10; ++i)
v.emplace_back(7);
}
and here I want to create 10 values and each to value ctor pass the integer 7 ...
std::vector< value > v(in-place, 10, 7)
Why wasn't the C++11 placement construction form added to this std::vector constructor
Errors pasted from coliru:
+ g++ -std=c++11 -O2 -Wall -pedantic -pthread main.cpp
In file included from /usr/include/c++/4.8/vector:62:0,
from main.cpp:3:
/usr/include/c++/4.8/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = value; _Args = {value}]’:
/usr/include/c++/4.8/bits/stl_uninitialized.h:75:53: required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<value*> _ForwardIterator = value*; bool _TrivialValueTypes = false]’
/usr/include/c++/4.8/bits/stl_uninitialized.h:117:41: required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<value*> _ForwardIterator = value*]’
/usr/include/c++/4.8/bits/stl_uninitialized.h:258:63: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::move_iterator<value*> _ForwardIterator = value*; _Tp = value]’
/usr/include/c++/4.8/bits/stl_vector.h:1142:29: 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 = std::move_iterator<value*> _Tp = value; _Alloc = std::allocator<value> std::vector<_Tp, _Alloc>::pointer = value*; std::vector<_Tp, _Alloc>::size_type = long unsigned int]’
/usr/include/c++/4.8/bits/vector.tcc:75:70: required from ‘void std::vector<_Tp, _Alloc>::reserve(std::vector<_Tp, _Alloc>::size_type) [with _Tp = value; _Alloc = std::allocator<value> std::vector<_Tp, _Alloc>::size_type = long unsigned int]’
main.cpp:24:17: required from here
/usr/include/c++/4.8/bits/stl_construct.h:75:7: error: use of deleted function ‘value::value(const value&)’
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^
main.cpp:11:5: error: declared here
value(value const&) = delete;
^
The reason you get errors is that using emplace_back on a std::vector requires the element type to be at least MoveConstructible. This is needed if the vector needs to grow and reallocate its elements.
Add a move constructor to you struct and you will be able to use it in your code (the default implementation will suffice for your code).
value(value&&) = default;
The compiler will not implicitly generate a default move constructor for your struct as you have declared your own copy constructor, value(value const&) = delete (=delete and =default count as user-declared), as well as a copy assignment operator and a destructor.
For more info about the rules of implicit move constructor generation, look here: Why no default move-assignment/move-constructor?
Using std::vector's constructor of the form std::vector(size_t count, const T& value) copies the values into the vector, and requires the element type to be CopyConstructible.
Yes, the resizing etc. can all be handled with move instead of copy constructor/assignment, if you define it (whatever cppreference says).
#include <vector>
#include <iostream>
struct value {
int i_;
explicit value(int i) : i_(i) {std::cout<<"value::value(" <<i_<<")\n"; }
value(value &&src) : i_(src.i_) {std::cout<<"value::value(&&"<<i_<<")\n"; }
value(value const&) = delete;
value& operator=(value const&) = delete;
value& operator=(value &&src) {
i_ = src.i_;
std::cout << "value::=(&&" << i_ << ")\n";
return *this;
}
value() = delete;
};
int main() {
std::vector<value> v;
v.reserve(1);
v.emplace_back(1);
v.emplace_back(2);
v.emplace_back(3);
}
works fine:
value::value(1) <-- emplace_back(1)
value::value(2)
value::value(&&1) <-- emplace_back(2) inc. resize & move 1
value::value(3)
value::value(&&1)
value::value(&&2) <-- emplace_back(3) inc. resize & move 1,2
Motivation for the requirement
Before move support, if a vector's elements were non-copyable, it would be unable to resize itself (or erase items, or fulfill quite a lot of its interface).
Yes, you can put objects that aren't copyable or default-constructible in a vector in C++11, if they are movable:
#include <iostream>
#include <string>
#include <vector>
struct value {
value() = delete;
~value() = default;
value(value const&) = delete;
value& operator =(value const&) = delete;
// Move construction and assignment
value(value&&) = default;
value& operator =(value&&) = default;
explicit value(int i) : i_(i) {}
private:
int i_;
};
int main() {
std::vector<value> v;
v.reserve(10);
for (unsigned i = 0; i < 10; ++i)
v.emplace_back(7);
}
But you can't use the constructor that fills such a vector with copies of a given value - since the values are not copyable. Similarly, you can't grow a vector of such objects with resize.
Write a move constructor and then use emplace_back:
struct value
{
...
value(value && obj) : i_(obj.i_)
{
}
or
value(value && obj) = default;
...
};
std::vector<value> v;
for (int i=0; i<10; i++)
v.emplace_back(7);

How can I get this code involving unique_ptr to compile?

#include <vector>
#include <memory>
using namespace std;
class A {
public:
A(): i(new int) {}
A(A const& a) = delete;
A(A &&a): i(move(a.i)) {}
unique_ptr<int> i;
};
class AGroup {
public:
void AddA(A &&a) { a_.emplace_back(move(a)); }
vector<A> a_;
};
int main() {
AGroup ag;
ag.AddA(A());
return 0;
}
does not compile... (says that unique_ptr's copy constructor is deleted)
I tried replacing move with forward. Not sure if I did it right, but it didn't work for me.
[~/nn/src] g++ a.cc -o a -std=c++0x
/opt/local/include/gcc44/c++/bits/unique_ptr.h: In member function 'A& A::operator=(const A&)':
a.cc:6: instantiated from 'void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, _Args&& ...) [with _Args = A, _Tp = A, _Alloc = std::allocator<A>]'
/opt/local/include/gcc44/c++/bits/vector.tcc:100: instantiated from 'void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = A, _Tp = A, _Alloc = std::allocator<A>]'
a.cc:17: instantiated from here
/opt/local/include/gcc44/c++/bits/unique_ptr.h:219: error: deleted function 'std::unique_ptr<_Tp, _Tp_Deleter>& std::unique_ptr<_Tp, _Tp_Deleter>::operator=(const std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = int, _Tp_Deleter = std::default_delete<int>]'
a.cc:6: error: used here
In file included from /opt/local/include/gcc44/c++/vector:69,
from a.cc:1:
/opt/local/include/gcc44/c++/bits/vector.tcc: In member function 'void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, _Args&& ...) [with _Args = A, _Tp = A, _Alloc = std::allocator<A>]':
/opt/local/include/gcc44/c++/bits/vector.tcc:100: instantiated from 'void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = A, _Tp = A, _Alloc = std::allocator<A>]'
a.cc:17: instantiated from here
/opt/local/include/gcc44/c++/bits/vector.tcc:314: note: synthesized method 'A& A::operator=(const A&)' first required here
Probably your standard library doesn't (yet) define unique_ptr<T>::unique_ptr(unique_ptr &&). I checked my headers in 4.5 and it's there, so maybe try upgrading.
When it fails to find the move constructor, it would look for the copy constructor and find it deleted.
I get other errors when I compile that, though.
EDIT: Got it to work. I don't understand why you have to move an object which is already an rvalue reference, but you do. The only problem was a missing assigment operator.
#include <vector>
#include <memory>
using namespace std;
class A {
public:
A(): i(new int) {}
A(A const& a) = delete;
A &operator=(A const &) = delete;
A(A &&a): i(move(a.i)) {}
A &operator=(A &&a ) { i = move(a.i); }
unique_ptr<int> i;
};
class AGroup {
public:
void AddA(A &&a) { a_.emplace_back(move(a)); }
vector<A> a_;
};
int main() {
AGroup ag;
ag.AddA(A());
return 0;
}