Why does this std::vector::emplace_back fail? - c++

I'm coming across a compiler error that says:
attempting to reference a deleted function
#include <iostream>
#include <vector>
template <typename T>
struct Container
{
Container() = default;
Container(const Container& other) = delete;
Container(T* ptr) : ptr(ptr) {}
T* ptr;
~Container() { delete ptr; }
};
struct Foo { Foo(int a, int b) {} };
int main()
{
std::vector<Container<Foo>> myvector;
myvector.push_back(new Foo(1, 2)); // I understand why this doesn't work.
myvector.emplace_back((new Foo(1, 2))); // I don't understand why this fails
}
I understand why it says attempting to reference a deleted constructor when I do std::vector::push_back(), because this does a copy and needs to call the copy constructor, which I deleted.
But std::vector::emplace_back() is supposed to take the constructor arguments of the type it holds. When I emplace back, I give it a pointer to a Foo, and this should be forwarded to the Container::Container(T* ptr) constructor.
What am I missing?

Declaring a User-Defined copy constructor will not define an implicit move constructor; T must either have a copy constructor or a move constructor to push_back or emplace_back* an object into a std::vector<T>.
From the docs, see the requirements on T to instantiate a std::vector<T>. (No restriction here, read on) ..emphasis mine
The requirements that are imposed on the elements depend on the actual operations performed on the container. Generally, it is required that element type meets the requirements of Erasable, but many member functions impose stricter requirements. This container (but not its members) can be instantiated with an incomplete element type if the allocator satisfies the allocator completeness requirements.
From std::vector<...>::push_back:
Type requirements
T must meet the requirements of CopyInsertable in order to use overload (1).
T must meet the requirements of MoveInsertable in order to use overload (2).
From std::vector<...>::emplace_back:
Type requirements
T (the container's element type) must meet the requirements of MoveInsertable and EmplaceConstructible.
For emplace_back here, your code would fulfill the EmplaceConstructible criteria, however, because reallcations can happen, you must equally fulfill MoveInsertable.

Related

Why does std::map::operator[] assignment require an argumentless constructor?

I have the following minimal example reproducing an error in my code:
#include <unordered_map>
#include <iostream>
class B
{
public:
B(int b) : m_b{ b } {}
int m_b;
};
int main()
{
using std::cout, std::endl;
std::unordered_map<int, B> ab{};
ab[1] = B(3);
//ab.insert(std::pair<int, B>(1, B(3)));
cout << ab[1].m_b << endl;
}
This fails with a long and unwieldy error which basically amounts to saying that there is no constructor for B without any arguments. The error stems from ab[1] = B(3) Why is that needed? And why does using insert instead of operator[] not need that constructor?
Bonus points for why this line in my original code:
Vec2 pos{ m_orbits[&p].positionAtTime(m_time + dt) };
also requires a non - parameterized constructor. I could not reproduce that error in my minimal example, but m_orbits is an unordered map with pointers to Particle objects as keys and Orbit objects as values. positionAtTime is a const member function of Orbit that calculates the position of a particle in the orbit at a certain time.
Why is [a constructor for B without any arguments] needed?
This is because std::map::operator[] required the mapped_type (i.e. in your case B) to be default constructable.
Inserts value_type(key, T()) if the key does not exist. This function is equivalent to return insert(std::make_pair(key, T())).first->second;
key_type must meet the requirements of CopyConstructible.
mapped_type must meet the requirements of CopyConstructible and DefaultConstructible.
If an insertion is performed, the mapped value is value-initialized (default-constructed for class types, zero-initialized otherwise) and a reference to it is returned.
When you provide a user defined constructor (i.e. B(int b)), the compiler will not generate a default constructor automatically, and thereby A can not be default construable.
If some user-declared constructors are present, the user may still force the automatic generation of a default constructor by the compiler that would be implicitly-declared otherwise with the keyword default.
Hence, the above error!
why does using insert instead of operator[] not need that constructor?
Because std::map::insert relies on the value_type (i.e. std::pair<const Key, T>). For your ab this is std::pair<const int, B>. From the cppreference.com the function overloads:
1-3) Inserts value. The overload (2) is equivalent to emplace(std::forward<P>(value)) and only participates in overload resolution if std::is_constructible<value_type, P&&>::value == true.
4-6) Inserts value in the position as close as possible, just prior(since C++11), to hint. The overload (5) is equivalent to emplace_hint(hint, std::forward<P>(value)) and only participates in overload resolution if std::is_constructible<value_type, P&&>::value == true.
So as long as B is constructable the std::pair<const int, B> also can be constructed and std::map::insert can be work.

Efficient direct initialization of a std::vector

I have a struct, say
struct A {
A(int n) : n(n) {}
int n;
};
and I want to initialize a std::vector with some elements. I can do this by using an initialization list, or by emplacing the new elements:
// 1st version: 3 ctors, 3 copy ctors, 3 dtors
std::vector<A> v1{1, 2, 3};
// 2nd version: 3 ctors
std::vector<A> v2;
v2.reserve(3);
v2.emplace_back(4);
v2.emplace_back(5);
v2.emplace_back(6);
As the comments show, the first version calls 3 constructors, 3 copy constructors, and 3 destructors. The version with emplace only uses 3 constructors.
Question: Obviously the second version is better, but the first version is more succinct. Can I have the best of both worlds? Can I do a direct initialization without the extra cost?
(Here's a longer version of the A struct that shows what's happening.)
Since A is convertible from int, you can use the range constructor of vector:
auto inits = {1, 2, 3};
std::vector<A> v1{std::begin(inits), std::end(inits)};
Or in a single declaration-statement (assuming you can rely on RVO):
auto v1 = [inits={1, 2, 3}] { return std::vector<A>{std::begin(inits), std::end(inits)}; }();
Expanding on #ecatmur's answer, I've developed a piece of code that allows a very general solution for any type of vector and for any constructor calls. The constructor parameters for each element of the vector are stored in a tuple (of & or && as appropriate) which are then perfect-forwarded when the element is built. Each element is constructed only once, essentially equivalent to emplace_back. This forwarding would even allow a vector of move-only types to be built such as unique_ptr<?>.
(Update, due to RVO it should simply construct them in place. Unfortunately, however, the element type does require at least a copy-constructor or move-constructor to be visible, even if they are actually skipped by the optimizer. This means you can build a vector of unique_ptr, but not of mutex.)
auto v2 = make_vector_efficiently<A>(
pack_for_later(1) // 1-arg constructor of A
,pack_for_later(2,"two") // 2-arg constructor of A
,pack_for_later(3) // 1-arg constructor of A
);
The above code will create a vector<A> with three elements. In my example, A has two constructors, one which takes int,string as parameters.
pack_for_later builds a tuple that stores its parameters as &/&& references. That is then converted into on object (of type UniquePointerThatConverts, that has the desired conversion operator, in this case operator A().
Within make_vector_efficiently, an initializer list of these converter objects is built and then vector is constructed with the begin() and
end() of the initializer_list. You might expect that these iterators would be required to have type T* in order to construct a vector<T>, but it is sufficient that the type the iterator points to can convert to T.
The constructor then uses placement new to (copy-)construct from the converted object. But, thanks for RVO, the copy won't happen and the converter will be effectively doing the equivalent of emplace_back for us.
Anyway, any feedback appreciated. Finally, it's trivial to extend this to other containers besides vector.
Full code on Coliru Should work on any C++11 compiler.
Some more detailed notes, and copies of the important functions:
pack_for_later simply builds a std::tuple. The standard make_tuple isn't good enough as it ignores references. Each element of the tuple built by pack_for_later is a reference (& or && as appropriate, depending on whether the original parameter was an lvalue or rvalue)
template<typename ...T>
std:: tuple<T&&...> pack_for_later(T&&... args) {
// this function is really just a more
// 'honest' make_tuple - i.e. without any decay
return std:: tuple<T&&...> (std::forward<T>(args)...);
}
Next, make_vector_efficiently is the function that brings is all together. It's first parameter is the 'Target' type, the type of the elements in the vector we wish to create. The collection of tuples is converted into our special converter type UniquePointerThatConverts<Target> and the vector is constructed as discussed above.
template<typename Target, typename ...PackOfTuples>
auto make_vector_efficiently(PackOfTuples&&... args)
-> std::vector<Target>
{
auto inits = { UniquePointerThatConverts<Target>(std::forward<PackOfTuples>(args))...};
return std::vector<Target> {std::begin(inits), std::end(inits)};
}
Because A can have multiple constructors, and we want to be able to use any and all of them, pack_for_later can return many different types (don't forget about lvalues and rvalues too). But we need a single type to build the init list from. Therefore, we define a suitable interface:
template<typename Target>
struct ConvInterface {
virtual Target convert_to_target_type() const = 0;
virtual ~ConvInterface() {}
};
Each tuple is therefore converted to an object that implements this interface by make_Conv_from_tuple. It actually returns a unique_ptr to such an object which is then stored in a UniquePointerThatConverts that has the actual conversion operator. It is this type that is stored in the init list which is used to initialize the vector.
template<typename Target>
struct UniquePointerThatConverts {
std:: unique_ptr<ConvInterface<Target>> p; // A pointer to an object
// that implements the desired interface, i.e.
// something that can convert to the desired
// type (Target).
template<typename Tuple>
UniquePointerThatConverts(Tuple&& p_)
: p ( make_Conv_from_tuple<Target>(std:: move(p_)) )
{
//cout << __PRETTY_FUNCTION__ << endl;
}
operator Target () const {
return p->convert_to_target_type();
}
};
And, of course, the actual conversion operator which constructs from the pack.
template<typename Target, typename ...T>
struct Conv : public ConvInterface<Target> {
std::tuple<T...> the_pack_of_constructor_args;
Conv(std::tuple<T...> &&t) : the_pack_of_constructor_args(std:: move(t)) {}
Target convert_to_target_type () const override {
using idx = typename make_my_index_sequence<sizeof...(T)> :: type;
return foo(idx{});
}
template<size_t ...i>
Target foo(my_index_sequence<i...>) const {
// This next line is the main line, constructs
// something of the Target type (see the return
// type here) by expanding the tuple.
return {
std:: forward
< typename std:: tuple_element < i , std::tuple<T...> > :: type >
(std:: get<i>(the_pack_of_constructor_args))
...
};
}
};

Why std::vector requires operator =

I have a question about a class that we can store in vector.
What is the requirement that can be stored in a vector?
It seems that such class has to have assignment operator. But I am not sure if that's all or not.
Let me give you an example. class A has const int member. If I don't write operator =, it doesn't compile. But in this example, this operator does nothing. This program displays 10 and 20 correctly. It looks that operator = is required but not used in reality.
#include <iostream>
#include <vector>
class A {
public:
A(int a) : a_(a) {}
A& operator =(const A& a2) { return *this;} // Without this, compile fails.
void print() const {
std::cerr << a_ << std::endl;
}
private:
const int a_;
};
int main(int argc, char** argv) {
std::vector<A> v;
v.push_back(A(10));
v.push_back(A(20));
for (const A& a : v) a.print();
}
This might surprise you:
v.push_back(A(20));
v.push_back(A(10));
std::sort(begin(v), end(v));
There are aspects of vector itself that require assignability, though I don't know, offhand, which (and I can't tell by compiling your code, since my compiler doesn't complain when I remove operator=()). According to Wikipedia (which references the relevant portion of the '03 standard), elements must be CopyConstructible and Assignable.
EDIT: Coming back to this a day later, it seems forehead-slappingly obvious when std::vector requires Assignable — any time it has to move elements around. Add a call to v.insert() or v.erase(), for example, and the compile will fail.
If I don't write operator =, it doesn't compile.
That surprised me, so I had a look into the standard and I found:
Your example has an implicitly deleted copy constructor but should still compile if a conforming C++11 standard library is at hand.
The only expression that puts constraints on the type used by the vector in your example is push_back.
The push_back() method of a sequence container type X<T,A> with allocator A and value_type T, requires T to be:
CopyInsertable if an lvalue or const rvalue reference is passed
MoveInsertable if a non-const rvalue is passed
Which means it requires a valid copy constructor or (as in this case) a valid move constructor which will be implicitly present from your code. Therefore the compilation should not fail in any compiler with a valid C++11 standard library.
Operations that require the type, contained in a vector to be assignable:
Ancillary conditions
typdef std::vector<T> X;
X a,b;
X&& rv;
X::value_type t;
X::value_type&& u;
X::size_type n;
X::const_iterator p,q; // p = valid for a, q = valid and dereferencable
initializer_list<T> il;
[i,j) -> valid iterator-range
Pessimistic* list of operations
The operations, which require T to be assignable, if X is a vector, are:
Statement Requirement on T
a = b; CopyInsertable, CopyAssignable
a = rv; MoveInsertable, MoveAssignable
a = il; CopyAssignable
a.emplace(p, args); MoveInsertable, MoveAssignable
a.insert(p, t); CopyAssignable
a.insert(p, u); MoveAssignable
a.insert(p, n, t); CopyInsertable, CopyAssignable
a.insert(p, i, j); EmplaceConstructible[from *i], MoveInsertable, MoveAssignable
a.insert(p, il); -> a.insert(p, il.begin(), il.end());
a.erase(q); MoveAssignable
a.erase(q1,q2) MoveAssignable
a.assign(i,j); Assignable from *i
a.assign(il); -> a.assign(il.begin(), il.end());
a.assign(n,t) CopyAssignable
* = Pessimistic means that there may exist certain conditions for several requirements to actually come into effect. If you use one of the expressions listed above, your type T will likely be required to be assignable.
push_back on vector will make vector grow in memory, that means
old objects needs to be copied to new object via assignment operator=
hence you need assignment operator=.
The availability of copy constructor and assignment operator are guaranteed by the compiler if you don't create any of your own. (the correctness of default implementation, is a different story though). In your example, you almost don't have to overload A::opeartor=().
The problem here is not a "missing" operator=(), but that the default one cannot be used, because you have declared a const data member
const int a_;
The default compiler generated operator=() cannot assign value to a const member. So you have to overload your self:
A & A::opeartor=(const A & in)
{
*const_cast<int*>(&a_) = in.a_; // !!!you are expected to do this.
}
So, your version of A::operator=() that does nothing, although making the code compile, does't change the a_ value of the left hand operand,

Why does std::vector::get_allocator() return by-value?

To my surprise, I got an error when trying to use std::vector::get_allocator() with an non-copyable allocator. Why does std::vector::get_allocator() return by-value and not by-reference?
template<typename T>
class nc_allocator {
public:
using value_type = T;
nc_allocator(nc_allocator const&) = delete;
nc_allocator& operator=(nc_allocator const&) = delete;
// Other required members.
};
std::vector<int, nc_allocator<int>> v;
// boom: use of deleted function
// 'nc_allocator<T>::nc_allocator(const nc_allocator<T>&) [with T = int]'
v.get_allocator();
I got an error when trying to use std::vector::get_allocator() with an non-copyable allocator.
The standard forbids you to do so. The allocator requirements in 17.6.3.5 state that an allocator shall be copyable.
X a1(a); Shall not exit via an exception.
post: a1 == a
X a1(move(a)); Shall not exit via an exception.
post: a1 equals the prior value
of a.
So return by value is a correct way of returning an allocator, concerning the allocator requirements defined by the standard.
I'm not sure why this is a requirement but if noncopyable allocators would have been allowed, the empty base optimization would no more work.
The standard simply dictates so:
[C++11: 23.2.1/7]: [..] In all container types defined in this Clause, the
member get_allocator() returns a copy of the allocator used to construct the container or, if that allocator has been replaced, a copy of the most recent replacement [..]
I suspect #Vaughn's correct in his hypothesis that allocators are meant to be used as "handles".
By very loose analogy, would you ever write a non-copyable functor for use with standard algorithms?

Class containing auto_ptr stored in vector

In an answer to Is it safe to store objects of a class which has an std::auto_ptr as its member variable in std::vector? I stated that a class that contained an auto_ptr could be stored in a vector provided the class had a user-defined copy constructor.
There were several comment suggesting that this was not the case, so this question is an attempt to clear the issue up. Consider the following code:
#include <memory>
#include <vector>
using namespace std;
struct Z {};
struct A {
A( Z z )
: p( new Z(z) ) {}
A( const A & a )
: p( a.p.get() ? new Z( *a.p.get()) : 0 ) {}
// no assigment op or dtor defined by intent
auto_ptr <Z> p;
};
int main() {
vector <A> av;
Z z;
A a(z);
av.push_back( a );
av.push_back( A(z) );
av.clear();
}
Please examine the above & in your reply indicate where undefined
behaviour in the meaning of the C++ Standard could occur for this particular class used in this particular way. I am not interested whether the class is useful, well-behaved, sortable, or how it performs under exceptions.
Please also note that this is not a question about the validity of creating a vector of auto_ptrs - I am well aware of the issues regarding that.
Thanks all for your inputs on what in
retrospect is probably a rather silly
question. I guess I focussed too much
on the copy ctor & forgot about
assignment. The lucky winner of my
acceptance points (and points mean
prizes!) is litb for a typically
exhaustive explanation (sorry
earwicker)
Objects stored in containers are required to be "CopyConstructable" as well as "Assignable" (C++2008 23.1/3).
Your class tries to deal with the CopyConstructable requirement (though I'd argue it still doesn't meet it - I edited that argument out since it's not required and because it's arguable I suppose), but it doesn't deal with the Assignable requirement. To be Assignable (C++2008 23.1/4), the following must be true where t is a value of T and u is a value of (possibly const) T:
t = u returns a T& and t is equivalent to u
The standard also says in a note (20.4.5/3): "auto_ptr does not meet the CopyConstructible and Assignable requirements for Standard Library container elements and thus instantiating a Standard Library container with an auto_ptr results in undefined behavior."
Since you don't declare or define an assignment operator, an implicit one will be provided that uses the auto_ptr's assignment operator, which definitely makes t not equivalent to u, not to mention that it won't work at all for "const T u" values (which is what Earwicker's answer points out - I'm just pointing out the exact portion(s) of the standard).
Trying to put the list of places together that makes the example undefined behavior.
#include <memory>
#include <vector>
using namespace std;
struct Z {};
struct A {
A( Z z )
: p( new Z(z) ) {}
A( const A & a )
: p( a.p.get() ? new Z( *a.p.get()) : 0 ) {}
// no assigment op or dtor defined by intent
auto_ptr <Z> p;
};
int main() {
vector <A> av;
...
}
I will examine the lines up to the one where you instantiate the vector with your type A. The Standard has to say
In 23.1/3:
The type of objects stored in these components must meet the requirements of CopyConstructible types (20.1.3), and the additional requirements of Assignable types.
In 23.1/4 (emphasis mine):
In Table 64, T is the type used to instantiate the container, t is a value of T, and u is a value of (possibly const) T.
+-----------+---------------+---------------------+
|expression |return type |postcondition |
+-----------+---------------+---------------------+
|t = u |T& |t is equivalent to u |
+-----------+---------------+---------------------+
Table 64
In 12.8/10:
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. The implicitly-declared copy assignment operator for a class X will have the form
X& X::operator=(const X&)
if
each direct base class B of X has a copy assignment operator whose parameter is of type const B&,
const volatile B& or B, and
for all the nonstatic data members of X that are of a class type M (or array thereof), each such class type has a copy assignment operator whose parameter is of type const M&, const volatile M& or M.
Otherwise, the implicitly declared copy assignment operator will have the form
X& X::operator=(X&)
(Note the last and second last sentence)
In 17.4.3.6/1 and /2:
In certain cases (replacement functions, handler functions, operations on types used to instantiate standard library template components), the C++ Standard Library depends on components supplied by a C++ program. If these components do not meet their requirements, the Standard places no requirements on the implementation.
In particular, the effects are undefined in the following cases:
for types used as template arguments when instantiating a template component, if the operations on the type do not implement the semantics of the applicable Requirements subclause (20.1.5, 23.1, 24.1, 26.1). Operations on such types can report a failure by throwing an exception unless otherwise specified.
Now, if you look at the specification of auto_ptr you will note it has a copy-assignment operator that takes a non-const auto_ptr. Thus, the implicitly declared copy assignment operator of your class will also take a non-const type as its parameter. If you read the above places carefully, you will see how it says that instantiating a vector with your type as written is undefined behavior.
I don't think it's necessarily the case that the above code will even compile. Surely the implementor of std::vector is at liberty to require an assignment operator to be available, from const A&?
And having just tried it, it doesn't compile on Visual Studio C++ 2008 Service Pack 1:
binary '=' : no operator found which
takes a right-hand operand of type
'const A' (or there is no acceptable
conversion)
My guess is that, on the guidance of Herb Sutter, the container classes in VC++ make every effort to impose the standard requirements on their type parameters, specifically to make it hard to use auto_ptr with them. They may have overstepped the boundaries set by the standard of course, but I seem to remember it mandating true assignment as well as true copy construction.
It does compile in g++ 3.4.5, however.
Since the regular auto_ptr semantic could suggests that the ownership is passed during the copying, I would rather use here boost::scoped_ptr. Of course the assignment operator is missing.
What about the following?
cout << av[ 0 ] << endl;
Also, conceptually, a copy should leave the item copied from unchanged. This is being violated in your implementation.
(It is quite another thing that your original code compiles fine with g++ -pedantic ... and Comeau but not VS2005.)