Issue with map and unordered_map: call to deleted constructor of 'std::pair<const int, int>'

I just updated (and compiled) clang this afternoon, and a piece of code I had written simply does not work any longer.

It boils down to the following code:

struct Foo {};

void foo(unsigned u) {
std::unordered_map<unsigned, Foo> map;
map.insert(std::make_pair(u, Foo())); // the offending line, also occurs with a std::map
}

And I got a huge backtrace… eurk.

I managed to reduce the test case to:

#include // 1

template
struct Node {
V value;

template <typename… Args>
Node(Args&&… args): value(std::forward(args)…) {} // 8
};

void foo(std::pair<int const, int> const& p) {
Node<std::pair<int const, int>> node(p); // 12
}

Where is picked up from the MinGW implementation of gcc 4.5.2. I get this error:

unordered_map.cpp:8:25: error: call to deleted constructor of ‘std::pair<const int, int>’
Node(Args&&… args): value(std::forward(args)…) {}
^ ~~~~~~~~~~~~~~~~~~~~~~~~
unordered_map.cpp:12:35: note: in instantiation of function template specialization ‘Node<std::pair<const int, int> >::Node<const std::pair<const int, int> &>’ requested here
Node<std::pair<int const, int>> node(p);
^
/mingw/lib/gcc/mingw32/4.5.2/include/c++\bits/stl_pair.h:71:12: note: function has been explicitly marked deleted here
struct pair
^
1 error generated.

The full invocation (with the version of clang) and the preprocessed file can be found in attachment, for those interested.

I was wondering if this is a bug in the gcc 4.5.2 standard library or a bug in clang… and I’d really appreciate a work-around if anyone has one, since I’m pretty much stuck at this point.

– Matthieu

invocation.txt (6.56 KB)

unordered_map.i (37.5 KB)

As a baseline, this code compiles successfully when using libc++ (compile with -stdlib=libc++, though you’ll of course need to install libc++) & I seem to have a repro of your errors with libstdc++ so I’m playing around with that now.

I was wondering if this is a bug in the gcc 4.5.2 standard library or a bug in clang… and I’d really appreciate a work-around if anyone has one, since I’m pretty much stuck at this point.

As a baseline, this code compiles successfully when using libc++ (compile with -stdlib=libc++, though you’ll of course need to install libc++) & I seem to have a repro of your errors with libstdc++ so I’m playing around with that now.

I’ve investigated this a bit further with some help & I believe this is a bug in clang ToT surrounding implicit move constructor/assignment.

Given this:

struct foo {

void operator=(foo&&);

};

int main() {

foo g((foo()));

}

Clang’s output is:

    simple.cc:6:7: error: call to deleted constructor of 'foo'
      foo g((foo()));
          ^ ~~~~~~~
    simple.cc:1:8: note: function has been explicitly marked deleted here
    struct foo {
           ^

[g++ compiles this without error]

Clang seems to be implementing not providing implicit move operations by implementing them as deleted rather than as not present. This would be incorrect & produces the failure above.

The reason this came up in std::pair is a bit weird - it seems libstdc++'s std::pair has none of the big 5 (copy ctor, move ctor, copy assignment, move assignment, destructor) /except/ the move assignment operator. It does however have member function templates that, while not technically copy/move construction/assignment, are probably good enough for most cases. (returning a temporary probably should invoke the copy ctor of libstdc++'s pair, though, since it doesn’t have a real move ctor which it should have)

Anyway, the presence of the move assignment operator caused clang to provide a deleted move ctor rather than no move ctor at all.

So there’s the tale - I’ll look into a fix & you’d be welcome to file a bug (& assign it to me, though if someone else on the list really wants to take it I don’t mind), Matthieu. Thanks for bringing this up.

[side note: we should do a better job about the warning on “The Most Vexing Parse” (see the declaration of ‘g’ in the above example which required the extra () to be a variable declaration instead of a function declaration) - like the parantheses warning I think we should have a couple of notes suggesting the two ways to address the issue - most likely the user actually meant to declare a variable & should insert the extra (). But alternatively they can avoid the warning by removing the extra () after the ‘foo’ in the argument list.]

  • David

David Blaikie <dblaikie@gmail.com> writes:

    simple.cc:6:7: error: call to deleted constructor of 'foo'
foo g((foo())); ^ ~~~~~~~ simple.cc:1:8: note: function
has been explicitly marked deleted here struct foo { ^

[g++ compiles this without error]

Clang seems to be implementing not providing implicit move operations by
implementing them as deleted rather than as not present. This would be
incorrect & produces the failure above.

The reason this came up in std::pair is a bit weird - it seems libstdc++'s
std::pair has none of the big 5 (copy ctor, move ctor, copy assignment, move
assignment, destructor) /except/ the move assignment operator. It does
however have member function templates that, while not technically copy/move
construction/assignment, are probably good enough for most cases. (returning
a temporary probably should invoke the copy ctor of libstdc++'s pair,
though, since it doesn't have a real move ctor which it should have)

Anyway, the presence of the move assignment operator caused clang to provide
a deleted move ctor rather than no move ctor at all.

BTW, that error output looks somewhat similar to what I got when trying
to compile a file which included <boost/thread.hpp>; I filed a clang bug
here:

http://llvm.org/bugs/show_bug.cgi?id=10945

Thanks,

-Miles

I was wondering if this is a bug in the gcc 4.5.2 standard library or a bug in clang… and I’d really appreciate a work-around if anyone has one, since I’m pretty much stuck at this point.

As a baseline, this code compiles successfully when using libc++ (compile with -stdlib=libc++, though you’ll of course need to install libc++) & I seem to have a repro of your errors with libstdc++ so I’m playing around with that now.

I’ve investigated this a bit further with some help & I believe this is a bug in clang ToT surrounding implicit move constructor/assignment.

On further further investigation (& getting to what I think is the code that’s generating this - and, helpfully, quotes the standard that supports it) I believe this is by design & libstdc++ has a bug.

The relevant portion of the standard is 12.8[class.copy] paragraph 7:

… If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor
is defined as deleted; otherwise, it is defined as defaulted. …

libstdc++'s std::pair should be defining the move/copy ctors/assignment operators (there’s similar clauses for all these implicit definitions) explicitly if it wants to define the move assignment operator explicitly.

[Miles: sorry about that, I’m checking your bug/repro now, but I’d hazard it’s the same thing & a bug in boost rather than clang. I’ll update when I’ve looked at it some more]

I was wondering if this is a bug in the gcc 4.5.2 standard library or a
bug in clang... and I'd really appreciate a work-around if anyone has one,
since I'm pretty much stuck at this point.

As a baseline, this code compiles successfully when using libc++ (compile
with -stdlib=libc++, though you'll of course need to install libc++) & I
seem to have a repro of your errors with libstdc++ so I'm playing around
with that now.

I've investigated this a bit further with some help & I believe this is a
bug in clang ToT surrounding implicit move constructor/assignment.

On further further investigation (& getting to what I think is the code
that's generating this - and, helpfully, quotes the standard that supports
it) I believe this is by design & libstdc++ has a bug.
The relevant portion of the standard is 12.8[class.copy] paragraph 7:
... If the class definition declares a move constructor or move
assignment operator, the implicitly declared copy constructor
is defined as deleted; otherwise, it is defined as defaulted. ...
libstdc++'s std::pair should be defining the move/copy ctors/assignment
operators (there's similar clauses for all these implicit definitions)
explicitly if it wants to define the move assignment operator explicitly.

http://gcc.gnu.org/viewcvs?view=revision&revision=174464 changed some
things around here in May. Does it fix the bug you're looking at? It's
not in the gcc-4.6.x release branch, but we may be able to convince
them to include it in a future 4.6 release.

On further further investigation (& getting to what I think is the code
that’s generating this - and, helpfully, quotes the standard that supports
it) I believe this is by design & libstdc++ has a bug.
The relevant portion of the standard is 12.8[class.copy] paragraph 7:
… If the class definition declares a move constructor or move
assignment operator, the implicitly declared copy constructor
is defined as deleted; otherwise, it is defined as defaulted. …
libstdc++'s std::pair should be defining the move/copy ctors/assignment
operators (there’s similar clauses for all these implicit definitions)
explicitly if it wants to define the move assignment operator explicitly.

http://gcc.gnu.org/viewcvs?view=revision&revision=174464 changed some
things around here in May. Does it fix the bug you’re looking at? It’s
not in the gcc-4.6.x release branch, but we may be able to convince
them to include it in a future 4.6 release.

[Miles: sorry about that, I’m checking your bug/repro now, but I’d hazard
it’s the same thing & a bug in boost rather than clang. I’ll update when
I’ve looked at it some more]

Thanks David for investigating!

I had examined the constructors defined for pair but unfortunately skipped the assignment operators deeming they would have no influence :confused:

I was wondering if it could be a bug in libstdc++ since gcc 4.5.2 is quite old with regard to the FDIS and I recall things moved (!) a bit on this side.

Thanks Jeffrey for digging in the bug report about gcc.

I’m going to try and check libc++, it’s a no dependency projects so should not be too complicated.

– Matthieu

Jeffrey Yasskin <jyasskin@google.com>
writes:

http://gcc.gnu.org/viewcvs?view=revision&revision=174464 changed
some things around here in May. Does it fix the bug you're looking
at? It's not in the gcc-4.6.x release branch, but we may be able to
convince them to include it in a future 4.6 release.

FWIW, the gcc development trunk[*] still compiles both my boost test
case and David's very small test case:

   struct foo {
     void operator=(foo&&);
   } g((foo()));

... without error.

So presuming clang is correct, and this _should_ be an error, the
issue still seems to be present in gcc.

[*] or rather, the most recent Debian snapshot of it:
gcc (Debian 20110914-1) 4.7.0 20110914 (experimental) [trunk revision 178863]

Thanks,

-Miles