2068. std::pair not C++03-compatible with defaulted copy c'tor

Section: 23.4.2 [pairs.pair] Status: NAD Submitter: Daniel Krügler Opened: 2011-06-18 Last modified: 2016-02-10

Priority: Not Prioritized

View all other issues in [pairs.pair].

View all issues with NAD status.

Discussion:

The specification of the copy semantics of the C++03 version of std::pair is defined by the class synopsis in [lib.pairs]:

template <class T1, class T2>
struct pair {
  typedef T1 first_type;
  typedef T2 second_type;

  T1 first;
  T2 second;
  pair();
  pair(const T1& x, const T2& y);
  template<class U, class V> pair(const pair<U, V> &p);
};

The effect of this specification is, that the copy constructor is compiler-declared with the proper form depending on the contained member types. In particular, the instantiation of pair is well-formed with an element type that has a copy constructor with non-const first parameter type like specialzations of auto_ptr or any user-defined type like the following one:

struct A {
  A(A&){}
};

In contrast to container types which require CopyConstructible value types, the C++03 pair does support these, albeit unusual, element types.

The FDIS version of the std::pair specification does specify the same semantics by defaulting the copy and move constructor in 23.4.2 [pairs.pair]:

template <class T1, class T2>
struct pair {
  typedef T1 first_type;
  typedef T2 second_type;

  T1 first;
  T2 second;
  pair(const pair&) = default;
  pair(pair&&) = default;
  pair();
  […]
};

But according to the current core rules this makes the instantiation of e.g. std::pair<A, int> ill-formed, because of the const mismatch of the compiler-declared form of the copy constructor with that of the defaulted declaration.

Unfortunately there seems to be no simple library solution for this problem. If the defaulted declarations were removed, both copy c'tor and move c'tor would be deleted, because there exist user-declared copy assignment and move assignment operators in the FDIS. But these operations need to be user-defined to realize the wanted semantics of these operations for element types that are reference types. If core rules would not be changed to fix that, I see the following options:

  1. Intentionally decide to break the support for element types with non-const copy c'tors in pair.
  2. User-declare both copy and move ctor to at least support the instantiation of the pair specializations, but this would still not allow to copy them by the copy constructor.
  3. User-declare both the const and non-const copy ctors, the move ctor, and additionally the non-const copy assignment operator to support the instantiation of the pair specializations and of these members. This would support all element types as it did in C++03, but all copy/move members would be non-trivial.
  4. Intentionally decide to give up support for element types that are references for pair, but still keep the allocator support with the effect of removing all declarations of the special copy/move members. User code that needs to use tuple instead. But this would be a rather drastic step requiring further corrections of the draft, e.g. a change of the signature of the algorithm minmax (not the overload with the initializer_list) with a different return type.

This problem does not extend as backward-compatibility problem to tuple, because the TR1 specification did explicitly declare copy constructor and copy assignment operator via the "normal" form:

tuple(const tuple&);
tuple& operator=(const tuple&);

[Bloomington, 2011]

Closed as NAD.

This is an unfortunate change of behavior between C++03 and C++11, but is consistent with tuple. There is no desire to go to lengths supporting types like auto_ptr now that rvalue references are in the language.

There may be an issue for Core/EWG to look at, so that some simple =default syntax could be used that would do the right thing. If such a facility became availabile, LWG might revisit this issue.

Proposed resolution: