unordered_map emplace issue

234 views
Skip to first unread message

Filip Konvička

unread,
Jul 29, 2015, 2:44:52 AM7/29/15
to std-dis...@isocpp.org
Hi,

I think the following is correct, but both gcc (except 6) and clang
reject it:

#include <unordered_map>

struct X {
X(bool) {}
X(X const&) = delete;
const X& operator=(X const&) = delete;
};

typedef std::unordered_map<int,X> Map;

int main() {
Map m;
m.emplace(1, true);
return 0;
}

Could you please confirm that this is valid?

Thanks,
Filip

Ville Voutilainen

unread,
Jul 29, 2015, 2:51:58 AM7/29/15
to std-dis...@isocpp.org
It's valid, emplace requires EmplaceConstructible, and X fulfills that
requirement.
There are no conflicting requirements that I can find.

Ville Voutilainen

unread,
Jul 29, 2015, 2:54:17 AM7/29/15
to std-dis...@isocpp.org
msvc 2015 also accepts it.

David Krauss

unread,
Jul 29, 2015, 2:59:26 AM7/29/15
to std-dis...@isocpp.org

On 2015–07–29, at 2:44 PM, Filip Konvička <filip.k...@logis.cz> wrote:

int main() {
  Map m;
  m.emplace(1, true);
  return 0;
}

Could you please confirm that this is valid?

map::emplace attempts to construct a map::value_type, which is an instance of std::pair. The constructors of pair are a bit complicated, and you’re calling a signature like pair(int const&, X const&) which passes a temporary X.

This works, although it’s ugly:

m.emplace(std::piecewise_construct, std::forward_as_tuple( 1 ), std::forward_as_tuple( true ) );


Ville Voutilainen

unread,
Jul 29, 2015, 3:09:23 AM7/29/15
to std-dis...@isocpp.org
On 29 July 2015 at 09:59, David Krauss <pot...@mac.com> wrote:
> map::emplace attempts to construct a map::value_type, which is an instance
> of std::pair. The constructors of pair are a bit complicated, and you’re
> calling a signature like pair(int const&, X const&) which passes a temporary
> X.

I would expect the signature used to be this one:
template<class U, class V> EXPLICIT constexpr pair(U&& x, V&& y);

Filip Konvička

unread,
Jul 29, 2015, 3:19:50 AM7/29/15
to std-dis...@isocpp.org
> map::emplace attempts to construct a map::value_type, which is an
> instance of std::pair. The constructors of pair are a bit complicated,
> and you’re calling a signature like pair(int const&, X const&) which
> passes a temporary X.

I understand a STL implementation could be doing this internally, but my
expectation as a user is that the first argument to emplace be forwarded
to the key object constructor, and the rest to the value object
constructor. I would definitely not expect that X is copied or moved -
that is the purpose of emplace after all, isn't it?

Or are you saying that the standard says that a map value needs to be a
std::pair?

> This works, although it’s ugly:
>
> m.emplace(std::piecewise_construct, std::forward_as_tuple( 1 ),
> std::forward_as_tuple( true ) );
>
> http://coliru.stacked-crooked.com/a/6b6bc711feaaee54

Interesting, thanks, I'll give it a try.


Ville Voutilainen

unread,
Jul 29, 2015, 3:39:38 AM7/29/15
to std-dis...@isocpp.org
On 29 July 2015 at 10:19, Filip Konvička <filip.k...@logis.cz> wrote:
> I understand a STL implementation could be doing this internally, but my
> expectation as a user is that the first argument to emplace be forwarded to
> the key object constructor, and the rest to the value object constructor. I
> would definitely not expect that X is copied or moved - that is the purpose
> of emplace after all, isn't it?

Correct.

> Or are you saying that the standard says that a map value needs to be a
> std::pair?

The standard does indeed say that, but that's not a problem.

David Krauss

unread,
Jul 29, 2015, 3:58:04 AM7/29/15
to std-dis...@isocpp.org

On 2015–07–29, at 3:09 PM, Ville Voutilainen <ville.vo...@gmail.com> wrote:

I would expect the signature used to be this one:
template<class U, class V> EXPLICIT constexpr pair(U&& x, V&& y);

Hmm, Clang and GCC 5.1 seem to be getting tripped up because is_convertible unconditionally returns false for non-movable types. That should only have the effect of making the constructor template (spuriously) explicit, but instead they go astray because they never use is_constructible but use is_convertible as a proxy.

So, it is a Clang bug. However, there’s also a subtle defect in the explicitness of pair constructors and a more serious defect in is_convertible.


On 2015–07–29, at 3:19 PM, Filip Konvička <filip.k...@logis.cz> wrote:

I understand a STL implementation could be doing this internally, but my expectation as a user is that the first argument to emplace be forwarded to the key object constructor, and the rest to the value object constructor.  I would definitely not expect that X is copied or moved - that is the purpose of emplace after all, isn't it?

If there are two arguments, they should get forwarded to a std::pair constructor which handles both the key and the value. Exposing the existence of internal std::pairs is probably one of the bigger shortcomings of the containers library.

If either the key or the value needs more than one constructor argument, you’ll need forward_as_tuple.

Or are you saying that the standard says that a map value needs to be a std::pair?

std::[unordered_]map takes its first two template parameters and feeds them into std::pair to generate its map::value_type.

Ville Voutilainen

unread,
Jul 29, 2015, 4:43:17 AM7/29/15
to std-dis...@isocpp.org
On 29 July 2015 at 10:57, David Krauss <pot...@gmail.com> wrote:
> Hmm, Clang and GCC 5.1 seem to be getting tripped up because is_convertible
> unconditionally returns false for non-movable types. That should only have

They most likely trip up because they don't implement
http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4387.html

> So, it is a Clang bug. However, there’s also a subtle defect in the
> explicitness of pair constructors and a more serious defect in
> is_convertible.

I don't think there is any defect in is_convertible, and the defect in
pair constructors
has been handled by the aforementioned paper.

David Krauss

unread,
Jul 29, 2015, 5:04:45 AM7/29/15
to std-dis...@isocpp.org

> On 2015–07–29, at 4:43 PM, Ville Voutilainen <ville.vo...@gmail.com> wrote:
>
> I don't think there is any defect in is_convertible, and the defect in
> pair constructors
> has been handled by the aforementioned paper.

It looks like the defect in pair constructors was created by that paper. Let’s move that discussion to the other thread, though.

Ville Voutilainen

unread,
Jul 29, 2015, 5:12:30 AM7/29/15
to std-dis...@isocpp.org
Now you've lost me. What defect are you talking about, wrt pair constructors and
explicit?

David Krauss

unread,
Jul 29, 2015, 6:18:45 AM7/29/15
to std-dis...@isocpp.org

On 2015–07–29, at 5:12 PM, Ville Voutilainen <ville.vo...@gmail.com> wrote:

Now you've lost me. What defect are you talking about, wrt pair constructors and
explicit?

Yeah. The defect in C++11 and C++14, caused by the lack of EXPLICIT and requirement of implicit convertibility, hadn’t been mentioned yet in this thread. It’s probably worth noting that the problematic behavior is conforming to the published standards, and wasn’t really the fault of the implementations.

dyp

unread,
Jul 29, 2015, 6:32:27 AM7/29/15
to std-dis...@isocpp.org
Summing up N4387 and how this pair constructor relates to the issue of
emplacing into a map:

Neither emplace nor the pair constructor actually copies nor moves the
target type in this case. However, due to technical reasons, pair's
constructors require that the target type is movable (or copyable).
Therefore emplace fails.

The technical reason has to with protection against accidental use of
explicit conversions: Since pair in its constructors directly
initializes the two members (first, second) from the constructor
arguments, this construction can select explicit constructors of
first_type and second_type:

struct my_int {
explicit my_int(int);
};

void foo( pair<my_int, my_int> );

foo( make_pair(1, 2) ); // explicit conversion from int to my_int?

In order to prevent such scenarios, pair before N4387 restricted its
converting constructors (those constructors were implicit) to the cases
where the arguments are *implicitly convertible* to first_type and
second_type respectively.

However, as shown by David Krauss' other thread, *implicit
convertibility* is too strong here: we don't need the additional
copyable/movable requirement implied by is_convertible in this case.

Filip Konvička

unread,
Jul 29, 2015, 7:00:27 AM7/29/15
to std-dis...@isocpp.org
> Summing up N4387 and how this pair constructor relates to the issue of
> emplacing into a map:

Thanks for your explanation.

The std::piecewise_construct is acceptable workaround for me at this
point (thanks David). I assume that we would still have issues with
tuples, though.

typedef std::tuple<int, noncopyable> T;
typedef std::list<T> List;

In this case emplace_back would also fail, and there is probably no
simple cure for this similar to piecewise_construct (or is there?)


Filip Konvička

unread,
Jul 29, 2015, 7:22:13 AM7/29/15
to std-dis...@isocpp.org
> The std::piecewise_construct is acceptable workaround for me at this
> point (thanks David). I assume that we would still have issues with
> tuples, though.
>
> typedef std::tuple<int, noncopyable> T;
> typedef std::list<T> List;
>
> In this case emplace_back would also fail, and there is probably no
> simple cure for this similar to piecewise_construct (or is there?)

Interestingly, clang accepts this, as does gcc 6 and Visual Studio 2013.
GCC 5.2 and older reject it.

I wonder what differences there are in the implementations that lead to
this behavior. (I'm not asking anyone to go ahead and investigate, I'm
just curious...)


#include <tuple>
#include <list>

struct X {
X(bool) {}
X(X const&) = delete;
const X& operator=(X const&) = delete;
};

typedef std::tuple<int,X> T;
typedef std::list<T> List;

int main() {
List l;
l.emplace_back(1, true);
return 0;
}



Richard Smith

unread,
Jul 29, 2015, 3:22:53 PM7/29/15
to std-dis...@isocpp.org

On Jul 29, 2015 4:22 AM, "Filip Konvička" <filip.k...@logis.cz> wrote:
>
> > The std::piecewise_construct is acceptable workaround for me at this
> > point (thanks David).  I assume that we would still have issues with
> > tuples, though.
> >
> > typedef std::tuple<int, noncopyable> T;
> > typedef std::list<T> List;
> >
> > In this case emplace_back would also fail, and there is probably no
> > simple cure for this similar to piecewise_construct (or is there?)
>
> Interestingly, clang accepts this, as does gcc 6 and Visual Studio 2013. GCC 5.2 and older reject it.

This is most likely a library issue rather than a compiler issue, and (unlike gcc and msvc) clang is often used with several different standard library implementations. It would be useful to identify which one you are using when comparing behavior across implementations.

> I wonder what differences there are in the implementations that lead to this behavior. (I'm not asking anyone to go ahead and investigate, I'm just curious...)
>
>
> #include <tuple>
> #include <list>
>
>
> struct X {
> X(bool) {}
> X(X const&) = delete;
> const X& operator=(X const&) = delete;
> };
>
> typedef std::tuple<int,X> T;
>
> typedef std::list<T> List;
>
> int main() {
> List l;
> l.emplace_back(1, true);
> return 0;
>
> }
>
>
>

> --
>
> --- You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
> To post to this group, send email to std-dis...@isocpp.org.
> Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.

David Krauss

unread,
Jul 30, 2015, 3:05:14 AM7/30/15
to std-dis...@isocpp.org

On 2015–07–29, at 7:00 PM, Filip Konvička <filip.k...@logis.cz> wrote:

typedef std::tuple<int, noncopyable> T;
typedef std::list<T> List;

In this case emplace_back would also fail, and there is probably no simple cure for this similar to piecewise_construct (or is there?)

Yes, piecewise_construct works on tuples as well as pairs. Did you try?

David Krauss

unread,
Jul 30, 2015, 3:10:16 AM7/30/15
to std-dis...@isocpp.org

On 2015–07–30, at 3:05 PM, David Krauss <pot...@mac.com> wrote:

Yes, piecewise_construct works on tuples as well as pairs. Did you try?

Ugh, foot in mouth.

Why doesn’t this work? Was piecewise_construct only introduced to support scoped allocation?

David Krauss

unread,
Jul 30, 2015, 4:00:49 AM7/30/15
to std-dis...@isocpp.org

On 2015–07–30, at 3:10 PM, David Krauss <pot...@gmail.com> wrote:

Why doesn’t this work? Was piecewise_construct only introduced to support scoped allocation?

I suspect that implementation difficulty might have played a role. Here’s the gist of a solution (hereby in the public domain).

Does this require a proposal paper?

Reply all
Reply to author
Forward
0 new messages