2367. pair and tuple are not correctly implemented for is_constructible with no args

Section: 23.15.4.3 [meta.unary.prop] Status: C++17 Submitter: Howard Hinnant Opened: 2014-02-19 Last modified: 2017-07-30

Priority: 3

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

View all issues with C++17 status.

Discussion:

Consider:

struct X
{
  X() = delete;
};

int main()
{
  typedef std::pair<int, X> P;
  static_assert(!std::is_constructible<P>::value, "");
  static_assert(!std::is_default_constructible<P>::value, "");
  typedef std::tuple<int, X> T;
  static_assert(!std::is_constructible<T>::value, "");
  static_assert(!std::is_default_constructible<T>::value, "");
}

For me these static_asserts fail. And worse than that, even asking the question fails (as opposed to gets the wrong answer):

assert(!std::is_constructible<P>::value);

In file included from test.cpp:2:

error:
      call to deleted constructor of 'X'
   pair() : first(), second() {}
                     ^
note: function has been explicitly marked deleted here
    X() = delete;
    ^
1 error generated.

This can be solved by specializing is_constructible on pair and tuple for zero Args:

template <class T, class U>
struct is_constructible<pair<T, U>>
  : integral_constant<bool, is_default_constructible<T>::value &&
                            is_default_constructible<U>::value>
{};

template <class ...T>
struct is_constructible<tuple<T...>>
  : integral_constant<bool,
                      __all<is_default_constructible<T>::value...>::value>
{};

Now everything just works.

[2014-05-14, Daniel comments]

The proposed resolution is incomplete, because it wouldn't work for cv-qualified objects of pair or for references of them during reference-initialization.

I would like to point out that the approach suggested in N3739 can be easily extended to solve the problem without need to muddle with specializing is_constructible:

template<class U1 = T1, class U2 = T2,
  typename enable_if<
    is_default_constructible<U1>::value && is_default_constructible<U2>::value
  , bool>::type = false
>
constexpr pair();

The new wording proposal represents an alternative wording change that I would strongly prefer.

Previous resolution from Howard [SUPERSEDED]:

This wording is relative to N3936.

  1. Add to 23.4.3 [pairs.spec]:

    template <class T, class U>
    struct is_constructible<pair<T, U>>
      : integral_constant<bool, is_default_constructible<T>::value &&
                                is_default_constructible<U>::value>
    {};
    
  2. Add to 23.5.3.10 [tuple.special]:

    template <class ...T>
    struct is_constructible<tuple<T...>>
      : integral_constant<bool, see below>
    {};
    

    -?- The second argument to integral_constant shall be true if for each T, is_default_constructible<T>::value is true.

[2015-05, Lenexa]

STL: I object to this resolution due to British spelling of behavior
JW: we already have other places of this spelling
VV: the easy resolution is to remove the notes
MC: if that's all we want to change: put it in and make the editorial change of removing the note
VV: the other paper doesn't make any of these changes so it would be consistent
JW: this make me want even more the features of having constructors doing the Right Thing - I haven't written up the request to do something like that
VV: so it would be an aggregate reflecting the properties of the constituting types
JW: I should write that up
MC: any objection to move to ready? in favor: 16, opposed: 0, abstain: 1

Proposed resolution:

This wording is relative to N3936.

  1. Change 23.4.2 [pairs.pair] around p3 as indicated:

    constexpr pair();
    

    -3- Requires: is_default_constructible<first_type>::value is true and is_default_constructible<second_type>::value is true.

    -4- Effects: Value-initializes first and second.

    -?- Remarks: This constructor shall not participate in overload resolution unless is_default_constructible<first_type>::value is true and is_default_constructible<second_type>::value is true. [Note: This behaviour can be implemented by a constructor template with default template arguments — end note].

  2. Change 23.5.3.1 [tuple.cnstr] around p4 as indicated:

    constexpr tuple();
    

    -4- Requires: is_default_constructible<Ti>::value is true for all i.

    -5- Effects: Value initializes each element.

    -?- Remarks: This constructor shall not participate in overload resolution unless is_default_constructible<Ti>::value is true for all i. [Note: This behaviour can be implemented by a constructor template with default template arguments — end note].