1

If I create a struct with an explicit constructor

struct A { int x; explicit A(int x):x(x){}; }; 

And then use it as the mapped_type in a std::map, I am able to emplace with the piecewise constructor:

#include <map> std::map<int, A> foo; foo.emplace( std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple(10) ); 

But I when I try to use the move or template constructors, I get errors and can't compile:

foo.emplace(std::make_pair(2, 20)); // <-- doesn't work foo.emplace(3, 30); // <-- doesn't work 

What's going on here? Until now, I hadn't appreciated that there was much of a difference between these different usages. I guess, with the pair move constructor, it could make sense that there's an implicit conversion from std::pair<int, A>... but why does that have to happen in the template constructor? And then why not with the piecewise constructor??

I've poked around a bit, but the docs for std::map::emplace and for explicit don't really clarify this for me.

2
  • Compiler/version? Commented Dec 11, 2016 at 23:16
  • gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.3) Commented Dec 11, 2016 at 23:17

1 Answer 1

4

Before N4387, pair<T1, T2>'s constructors taking U/V do not exist unless U is implicitly convertible to T1 and V is implicitly convertible to T2:

template<class U, class V> constexpr pair(U&& x, V&& y); 

Requires: is_constructible<first_type, U&&>::value is true and is_constructible<second_type, V&&>::value is true.

Effects: The constructor initializes first with std::forward<U>(x) and second with std::forward<V>(y).

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.

Likewise for the const pair<U, V>& and pair<U, V>&& constructor.

Since those constructors effectively don't exist, your later two emplaces will not work.


N4387 changed the rules here so that instead those constructors become explicit if both types are constructible from the corresponding argument type but at least one is not implicitly convertible from the argument type. In C++17, therefore, all three emplaces will compile. Also, as the paper addresses a defect report raising pretty much this exact issue (among several), implementations may also choose to implement it in earlier standard modes.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.