As far as I can tell, the issue isn't caused by map::emplace
, but by pair
's constructors:
#include <map>
struct A
{
A(int) {}
A(A&&) = delete;
A(A const&) = delete;
};
int main()
{
std::pair<int, A> x(1, 4); // error
}
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
A t(4);
that is, std::is_constructible<A, int>::value == true
, we cannot implicitly convert an int
to an A
[conv]/3
An expression e
can be implicitly converted to a type T
if and only if the declaration T t=e;
is well-formed,
for some invented temporary variable t
.
Note the copy-initialization (the =
). This creates a temporary A
and initializes t
from this temporary, [dcl.init]/17. This initialization from a temporary tries to call the deleted move ctor of A
, which makes the conversion ill-formed.
As we cannot convert from an int
to an A
, the constructor of pair
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 constructor explicit
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
template<class U, class V> constexpr pair(U&& x, V&& y);
7 Requires: is_constructible<first_type, U&&>::value
is true
and
is_constructible<second_type, V&&>::value
is true
.
8 Effects: The
constructor initializes first with std::forward<U>(x)
and second with
std::forward<V>(y)
.
9 Remarks: If U
is not implicitly convertible to
first_type
or V
is not implicitly convertible to second_type
this
constructor shall not participate in overload resolution.
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 the int
argument and bound to this reference, and the reference is used to initialize the pair
data member (.second
). The initialization tries to call the deleted copy ctor of A
, 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:
template<class _U1, class _U2,
class = typename enable_if<__and_<is_convertible<_U1, _T1>,
is_convertible<_U2, _T2>
>::value
>::type>
constexpr pair(_U1&& __x, _U2&& __y)
: first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y)) { }
and the one that is actually called is the non-standard:
// DR 811.
template<class _U1,
class = typename enable_if<is_convertible<_U1, _T1>::value>::type>
constexpr pair(_U1&& __x, const _T2& __y)
: first(std::forward<_U1>(__x)), second(__y) { }
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
and is_convertible
.
is_constructible
[meta.rel]/4
Given the following function prototype:
template <class T>
typename add_rvalue_reference<T>::type create();
the predicate condition for a template specialization is_constructible<T, Args...>
shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t
:
T t(create<Args>()...);
[Note: These tokens are never interpreted as a function declaration. — end note] Access checking is performed as if in a context unrelated to T
and any of the Args
. Only the validity of the immediate context of the variable initialization is considered.
is_convertible
[meta.unary.prop]/6:
Given the following function prototype:
template <class T>
typename add_rvalue_reference<T>::type create();
the predicate condition for a template specialization is_convertible<From, To>
shall be satisfied if and
only if the return expression in the following code would be well-formed, including any implicit conversions
to the return type of the function:
To test() {
return create<From>();
}
[Note: This requirement gives well defined results for reference types, void types, array types, and function types. — end note] Access checking is performed as if in a context unrelated to To
and From
. Only
the validity of the immediate context of the expression of the return-statement (including conversions to
the return type) is considered.
For your type A
,
A t(create<int>());
is well-formed; however
A test() {
return create<int>();
}
creates a temporary of type A
and tries to move that into the return-value (copy-initialization). That selects the deleted ctor A(A&&)
and is therefore ill-formed.