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 anintto anA[conv]/3Note the copy-initialization (the
=). This creates a temporaryAand initializestfrom 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
intto anA, the constructor ofpairthat 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 constructorexplicitinstead 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_constructiblein 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 theintargument and bound to this reference, and the reference is used to initialize thepairdata 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
pairthat 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_constructibleandis_convertible.is_constructible[meta.rel]/4is_convertible[meta.unary.prop]/6:For your type
A,is well-formed; however
creates a temporary of type
Aand tries to move that into the return-value (copy-initialization). That selects the deleted ctorA(A&&)and is therefore ill-formed.