Anybody knows why the compiler needs a copy constructor for Foo in this situation:
#include <iostream>
#include <list>
class Foo {
public:
Foo() {}
Foo(const Foo &&f) noexcept {}
Foo(const Foo &f) = delete;
~Foo() {}
};
void passFoo2(const Foo&& f) {
std::list<Foo> l;
l.push_back(std::move(f));
}
int main(int argc, char **argv) {
Foo f;
passFoo2(std::move(f));
return 0;
}
The compiler (g++) complains the copy constructor is deleted.
But it should not need it in that case?
So what am I missing here?
x#ubuntu:/tmp/c++$ g++ stackoverflow.cxx -std=c++11
In file included from /usr/include/c++/4.9/list:63:0,
from stackoverflow.cxx:2:
/usr/include/c++/4.9/bits/stl_list.h: In instantiation of std::_List_node<_Tp>::_List_node(_Args&& ...) [with _Args = {const Foo&}; _Tp = Foo]:
/usr/include/c++/4.9/ext/new_allocator.h:120:4: required from void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::_List_node<Foo>; _Args = {const Foo&}; _Tp = std::_List_node<Foo>]
/usr/include/c++/4.9/bits/stl_list.h:514:8: required from std::list<_Tp, _Alloc>::_Node* std::list<_Tp, _Alloc>::_M_create_node(_Args&& ...) [with _Args = {const Foo&}; _Tp = Foo; _Alloc = std::allocator<Foo>; std::list<_Tp, _Alloc>::_Node = std::_List_node<Foo>]
/usr/include/c++/4.9/bits/stl_list.h:1688:63: required from void std::list<_Tp, _Alloc>::_M_insert(std::list<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {const Foo&}; _Tp = Foo; _Alloc = std::allocator<Foo>; std::list<_Tp, _Alloc>::iterator = std::_List_iterator<Foo>]
/usr/include/c++/4.9/bits/stl_list.h:1029:9: required from void std::list<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = Foo; _Alloc = std::allocator<Foo>; std::list<_Tp, _Alloc>::value_type = Foo]
stackoverflow.cxx:14:30: required from here
/usr/include/c++/4.9/bits/stl_list.h:114:71: error: use of deleted function Foo::Foo(const Foo&)
: __detail::_List_node_base(), _M_data(std::forward<_Args>(__args)...)
^
stackoverflow.cxx:8:3: note: declared here
Foo(const Foo &f) = delete;
^
the reason is that push_back has an overload for Foo&& not const Foo&&.
const Foo&& is superfluous. It's a legal type but its existence is an anachronism.
this compiles:
#include <iostream>
#include <list>
class Foo {
public:
Foo() {}
Foo( Foo &&f) noexcept {}
Foo(const Foo &f) = delete;
~Foo() {}
};
void passFoo2(Foo&& f) {
std::list<Foo> list;
list.push_back(std::move(f));
}
int main(int argc, char **argv) {
Foo f;
passFoo2(std::move(f));
return 0;
}
The push_back method has two overloads:
void push_back( const T& value );
void push_back( T&& value );
If you use the first one, T (Foo in your case) must be "CopyInsertable". If you use the second one, it must be "MoveInsertable".
Your passFoo2 function receives a const Foo&& reference, and since it is const-qualified (see note below), the first overload is the best match. So, that overload is called, which requires your class Foo to be copyable.
Note: Function std::move transform a named-object to make it anonymous, and so, suitable to be binded by a rvalue-reference, but it doesn't change const qualifications (it is a const Foo anonymous object after the movement). For this reason the first overload is called.
Related
This question already has answers here:
How do I call ::std::make_shared on a class with only protected or private constructors?
(19 answers)
Closed 8 months ago.
The community reviewed whether to reopen this question 8 months ago and left it closed:
Original close reason(s) were not resolved
I hope to prevent the users from creating new instance through the constructor, so I mark the constructor as a private method.
What's more, I need to provide a method to return an object which is used to automatically manage the life of the instance.The function in the code snippet below is getFoo().
Here is the code snippet which I wrote at first:
#include <iostream>
#include <memory>
class Foo : public std::enable_shared_from_this<Foo> {
private: //the user should not construct an instance through the constructor below.
Foo(int num):num_(num) { std::cout << "Foo::Foo\n"; }
public:
~Foo() { std::cout << "Foo::~Foo\n"; }
std::shared_ptr<Foo> getFoo() { return shared_from_this(9); }
private:
int num_;
};
Since the constructor is marked as private, so there is no way to create an instance, which causes getFoo could never be called. So I updated the code snippet above.
Here is the code snippet:
#include <iostream>
#include <memory>
class Foo : public std::enable_shared_from_this<Foo> {
private: //the user should not construct an instance through the constructor below.
Foo(int num):num_(num) { std::cout << "Foo::Foo\n"; }
public:
~Foo() { std::cout << "Foo::~Foo\n"; }
static std::shared_ptr<Foo> Create() { return std::make_shared<Foo>(5); }
private:
int num_;
};
int main() {
auto pf = Foo::Create();
}
But it does not compile, indeed.
Here is what the compiler complains:
In file included from /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/alloc_traits.h:33,
from /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/ext/alloc_traits.h:34,
from /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/basic_string.h:40,
from /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/string:53,
from /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/locale_classes.h:40,
from /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/ios_base.h:41,
from /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/ios:42,
from /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/ostream:38,
from /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/iostream:39,
from <source>:1:
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/stl_construct.h: In instantiation of 'void std::_Construct(_Tp*, _Args&& ...) [with _Tp = Foo; _Args = {int}]':
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/alloc_traits.h:635:19: required from 'static void std::allocator_traits<std::allocator<void> >::construct(allocator_type&, _Up*, _Args&& ...) [with _Up = Foo; _Args = {int}; allocator_type = std::allocator<void>]'
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/shared_ptr_base.h:604:39: required from 'std::_Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp>::_Sp_counted_ptr_inplace(_Alloc, _Args&& ...) [with _Args = {int}; _Tp = Foo; _Alloc = std::allocator<void>; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]'
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/shared_ptr_base.h:971:16: required from 'std::__shared_count<_Lp>::__shared_count(_Tp*&, std::_Sp_alloc_shared_tag<_Alloc>, _Args&& ...) [with _Tp = Foo; _Alloc = std::allocator<void>; _Args = {int}; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]'
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/shared_ptr_base.h:1712:14: required from 'std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = std::allocator<void>; _Args = {int}; _Tp = Foo; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]'
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/shared_ptr.h:464:59: required from 'std::shared_ptr<_Tp>::shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = std::allocator<void>; _Args = {int}; _Tp = Foo]'
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/shared_ptr.h:1009:14: required from 'std::shared_ptr<typename std::enable_if<(! std::is_array< <template-parameter-1-1> >::value), _Tp>::type> std::make_shared(_Args&& ...) [with _Tp = Foo; _Args = {int}; typename enable_if<(! is_array< <template-parameter-1-1> >::value), _Tp>::type = Foo]'
<source>:9:68: required from here
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/stl_construct.h:119:7: error: 'Foo::Foo(int)' is private within this context
119 | ::new((void*)__p) _Tp(std::forward<_Args>(__args)...);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:6:5: note: declared private here
6 | Foo(int num):num_(num) { std::cout << "Foo::Foo\n"; }
| ^~~
It really surprises that Create() could not call the private constructor. I think any member function should have the allowance to invoke another private member variable. If I miss something, please let me know.
Note: I only could use C++11.
Five minutes later, I realise where am I wrong. Create() is not a member function, it's a static function. It's legal that it could not call the non-static member function :(
You can make the shared yourself. Will this work for you?
#include <iostream>
#include <memory>
class Foo : public std::enable_shared_from_this<Foo> {
private: //the user should construct an instance through the constructor below.
Foo(int num):num_(num) { std::cout << "Foo::Foo\n"; }
public:
~Foo() { std::cout << "Foo::~Foo\n"; }
static std::shared_ptr<Foo> Create() {
Foo *foo = new Foo(5);
return std::shared_ptr<Foo>(foo);
}
private:
int num_;
};
int main() {
auto pf = Foo::Create();
}
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) });
}
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).
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);
#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;
}