19

The very simple code below compiles and links without a warning in C++98 but gives an incomprehensible compile error in C++11 mode.

#include <map> struct A { A(A& ); // <-- const missing }; int main() { std::map<int, A> m; return m.begin() == m.end(); // line 9 } 

The error with -std=c++11 is, gcc version 4.9.0 20140302 (experimental) (GCC):

 ali@X230:~/tmp$ ~/gcc/install/bin/g++ -std=c++11 cctor.cpp In file included from /home/ali/gcc/install/include/c++/4.9.0/bits/stl_algobase.h:64:0, from /home/ali/gcc/install/include/c++/4.9.0/bits/stl_tree.h:61, from /home/ali/gcc/install/include/c++/4.9.0/map:60, from cctor.cpp:1: /home/ali/gcc/install/include/c++/4.9.0/bits/stl_pair.h: In instantiation of ‘struct std::pair’: cctor.cpp:9:31: required from here /home/ali/gcc/install/include/c++/4.9.0/bits/stl_pair.h:127:17: error: ‘constexpr std::pair::pair(const std::pair&) [with _T1 = const int; _T2 = A]’ declared to take const reference, but implicit declaration would take non-const constexpr pair(const pair&) = default; ^ 

with clang version 3.5 (trunk 202594)

 ali@X230:~/tmp$ clang++ -Weverything -std=c++11 cctor.cpp In file included from cctor.cpp:1: In file included from /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/map:60: In file included from /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/bits/stl_tree.h:63: In file included from /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/bits/stl_algobase.h:65: /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/bits/stl_pair.h:119:17: error: the parameter for this explicitly-defaulted copy constructor is const, but a member or base requires it to be non-const constexpr pair(const pair&) = default; ^ cctor.cpp:9:22: note: in instantiation of template class 'std::pair' requested here return m.begin() == m.end(); // line 9 ^ 1 error generated. 

I have been looking at the code in bits/stl_tree.h and I don't understand why it is trying to instantiate std::pair.

Why does it need the copy constructor of std::pair in C++11?


Note: the above code was extracted from Equality operator (==) unsupported on map iterators for non-copyable maps.


SOLUTION

There are two unfortunate issues here.

Poor quality error messages: Line 8 should already give a compile error although the error messages are only complaining about line 9 . Getting an error on line 8 would be quite helpful and understanding the real problem would be much easier. I will probably submit a bug report / feature request if this issue is still present in gcc / clang trunk.

The other issue is what ecatmur writes. Consider the following code:

struct A { A() = default; A(A& ); // <-- const missing }; template<class T> struct B { B() = default; B(const B& ) = default; T t; }; int main() { B<A> b; } 

It fails to compile. Even though the copy constructor is not needed anywhere, it is still instantiated because it is defaulted inline, in the body of the class; this leads to the compile error. This can be fixed by moving the copy constructor out of the body of the class:

template<class T> struct B { B() = default; B(const B& ); T t; }; template <class T> B<T>::B(const B& ) = default; 

Everything is OK then. Unfortunately, std::pair has a default defined inline copy constructor.

6
  • 1
    Hmm at coliru it already fails when the map is just defined, no need to include the comparison or begin/end. Commented Mar 12, 2014 at 18:03
  • in this example too, it fails if map is just declared Commented Mar 12, 2014 at 18:04
  • @dyp Correct. That would have been a liitle bit more obvious then. Strange that the error message only barks at the iterator comparison. Commented Mar 12, 2014 at 18:08
  • 1
    std::pair<int, A> p; is enough. Commented Mar 12, 2014 at 18:30
  • 1
    clang/libc++ has no problem: coliru.stacked-crooked.com/a/d9546aed80a496c3 (note that your output indicates that you're using clang with gcc 4.7 library) Commented Mar 12, 2014 at 21:52

3 Answers 3

4

The copy constructor of std::pair isn't needed in this case, but because it is default defined inline in the declaration of std::pair, it is automatically instantiated along with the instantiation of std::pair itself.

It would be possible for the standard library to provide a non-inline default definition of the copy constructor:

template<class _T1, class _T2> struct pair { // ... constexpr pair(const pair&); // ... }; // ... template<class _T1, class _T2> constexpr pair<_T1, _T2>::pair(const pair&) = default; 

However this would not accord with the strict letter of the standard (clause 20.3.2), where the copy constructor is default defined inline:

 constexpr pair(const pair&) = default; 
Sign up to request clarification or add additional context in comments.

12 Comments

Okay, thanks. Looks like this is the answer. Just one more question: Is it allowed by the standard that the compiler only barks at the iterator comparison? If I comment out the return m.begin() == m.end(); line then it is obvious that it cannot instantiate the map itself. Barking at the iterator comparison is just confusing... :(
@Ali the error in the instantiation of the map itself is delayed because it goes through a non-inline member function (_Rb_tree<...>::_M_erase(...)). Unfortunately the standard doesn't require that error messages ("diagnostics") be particularly useful; compilers have gotten quite a bit better at this recently (it's called QOI, or quality of implementation).
Sorry, why is it automatically instantiated? For other template methods, even if the function is written inside the class, it is only instantiated if called. Is there verbage saying that =default instantiates without being called now?
@ecatmur OK, let me rephrase that: Is it allowed that the diagnostic complains only about the second line but not the first line that wouldn't compile? I have always thought that at least the first line that fails is in the error message.
@Ali no, it's entirely up to the compiler the order in which to emit error messages.
|
2

std::map uses std::pair to store key-value pairs, where the key (the first element) is const.

The compiler error relates to the required copy constructor for std::pair, even if it isn't being used (which I don't think it is).

std::pair<int, A> has to be generated. This is first required with the call to map::begin. Since no explicit copy constructor is given for this type, the implicit one used.

The implicit constructor will have signature T::T(const T&) only if all non-static members of T, (type S), have copy constructors S::S(const S&) (the same requirement has to hold for T's base types copy constructors). Otherwise a copy constructor with signature T::T(T&) is used instead.

A's copy constructor fails this requirement, so std::pair::pair has the wrong signature for the STL, which requires T::T(const T&).

3 Comments

I don't see how it answers the question.
Agreed. Why does a copy need to be made at all?
Was going the wrong way anyway, edited my answer for what I think is going on.
2

I think I found it after trying to reduce the error. First, the comparison doesn't seem required to make the program ill-formed. Then, the error message contained the dtor, so I tried not to instantiate the dtor. Result:

#include <map> struct A { A(A& ); // <-- const missing }; int main() { std::map<int, A>* m = new std::map<int, A>(); // note: dtor not (necessarily?) instantiated } 

But the output message still contains, now for the line where the ctor of m is called:

error: the parameter for this explicitly-defaulted copy constructor is const, but a member or base requires it to be non-const

 constexpr pair(const pair&) = default; 

Which hints to [dcl.fct.def.default]/4

A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted; if such a function is implicitly defined as deleted, the program is ill-formed.

[emphasis mine]

If, as I assume, [class.copy]/11 says that this ctor should be defined as deleted, then it is defined as deleted immediately - not only when it's odr-used. Therefore, an instantiation shouldn't be required to make the program ill-formed.

6 Comments

I think [class.copy]/11 should imply that this function is deleted; however although it mentions overload resolution, it does not explicitly say that the function must be viable.
Okay, thanks. If you look at the error messages I posted, you will see that those messages also contain the same error message as you get. One more question: In case of the code posted in the question, is it allowed by the standard that the compiler only barks at the iterator comparison?
@Ali Hmm I think once the program is ill-formed, only one diagnostic message is required (and I don't think there are requirements on its content).
I would have expected to get an error message for the very first line that fails to compile. I understand that only one error message is required but is it allowed to complain only about the second problematic line? This leads to confusions and find it a very poor error message.
Alright then. I will probably submit a bug report / feature request because I find it very confusing.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.