The following code will not compile on gcc 4.8.2.
The problem is that this code will attempt to copy construct an std::pair<int, A>
which can't happen due to struct A
missing copy and move constructors.
Is gcc failing here or am I missing something?
#include <map>
struct A
{
int bla;
A(int blub):bla(blub){}
A(A&&) = delete;
A(const A&) = delete;
A& operator=(A&&) = delete;
A& operator=(const A&) = delete;
};
int main()
{
std::map<int, A> map;
map.emplace(1, 2); // doesn't work
map.emplace(std::piecewise_construct,
std::forward_as_tuple(1),
std::forward_as_tuple(2)
); // works like a charm
return 0;
}
As far as I can tell, the issue isn't caused by
map::emplace
, but bypair
's constructors:This code example doesn't compile, neither with coliru's g++4.8.1 nor with clang++3.5, which are both using libstdc++, as far as I can tell.
The issue is rooted in the fact that although we can construct
that is,
std::is_constructible<A, int>::value == true
, we cannot implicitly convert anint
to anA
[conv]/3Note the copy-initialization (the
=
). This creates a temporaryA
and initializest
from this temporary, [dcl.init]/17. This initialization from a temporary tries to call the deleted move ctor ofA
, which makes the conversion ill-formed.As we cannot convert from an
int
to anA
, the constructor ofpair
that one would expect to be called is rejected by SFINAE. This behaviour is surprising, N4387 - Improving pair and tuple analyses and tries to improve the situation, by making the constructorexplicit
instead of rejecting it. N4387 has been voted into C++1z at the Lenexa meeting.The following describes the C++11 rules.
The constructor I had expected to be called is described in [pairs.pair]/7-9
Note the difference between
is_constructible
in the Requires section, and "is not implicitly convertible" in the Remarks section. The requirements are fulfilled to call this constructor, but it may not participate in overload resolution (= has to be rejected via SFINAE).Therefore, overload resolution needs to select a "worse match", namely one whose second parameter is a
A const&
. A temporary is created from theint
argument and bound to this reference, and the reference is used to initialize thepair
data member (.second
). The initialization tries to call the deleted copy ctor ofA
, and the construction of the pair is ill-formed.libstdc++ has (as an extension) some nonstandard ctors. In the latest doxygen (and in 4.8.2), the constructor of
pair
that I had expected to be called (being surprised by the rules required by the Standard) is:and the one that is actually called is the non-standard:
The program is ill-formed according to the Standard, it is not merely rejected by this non-standard ctor.
As a final remark, here's the specification of
is_constructible
andis_convertible
.is_constructible
[meta.rel]/4is_convertible
[meta.unary.prop]/6:For your type
A
,is well-formed; however
creates a temporary of type
A
and tries to move that into the return-value (copy-initialization). That selects the deleted ctorA(A&&)
and is therefore ill-formed.