Title
Still too many implicit conversions for pair and tuple
Status
resolved
Section
[pairs.pair][tuple.cnstr]
Submitter
Daniel Krügler

Created on 2010-03-20.00:00:00 last changed 170 months ago

Messages

Date: 2011-05-03.22:13:16

Proposed resolution:

See n3140.

Date: 2010-10-24.00:00:00

[ 2010-10-24 Daniel adds: ]

Accepting n3140 would solve this issue.

Date: 2010-11-11.03:10:11

[ Only constraints on constructors are suggested. Adding similar constraints on assignment operators is considered as QoI, because the assigments wouldn't be well-formed anyway. ]

  1. Following [pairs.pair]/5 add a new Remarks element:

    template<class U, class V> pair(const pair<U, V>& p);
    

    5 Effects: Initializes members from the corresponding members of the argument, performing implicit conversions as needed.

    Remarks: This constructor shall not participate in overload resolution unless U is implicitly convertible to first_type and V is implicitly convertible to second_type.

  2. Following [pairs.pair]/6 add a new Remarks element:

    template<class U, class V> pair(pair<U, V>&& p);
    

    6 Effects: The constructor initializes first with std::move(p.first) and second with std::move(p.second).

    Remarks: This constructor shall not participate in overload resolution unless U is implicitly convertible to first_type and V is implicitly convertible to second_type.

  3. Following [tuple.cnstr]/7 add a new Remarks element:

    template <class... UTypes>
    explicit tuple(UTypes&&... u);
    

    6 Requires: Each type in Types shall satisfy the requirements of MoveConstructible (Table 33) from the corresponding type in UTypes. sizeof...(Types) == sizeof...(UTypes).

    7 Effects: Initializes the elements in the tuple with the corresponding value in std::forward<UTypes>(u).

    Remarks: This constructor shall not participate in overload resolution unless each type in UTypes is implicitly convertible to its corresponding type in Types.

  4. Following [tuple.cnstr]/13 add a new Remarks element:

    template <class... UTypes> tuple(const tuple<UTypes...>& u);
    

    12 Requires: Each type in Types shall be constructible from the corresponding type in UTypes. sizeof...(Types) == sizeof...(UTypes).

    13 Effects: Constructs each element of *this with the corresponding element of u.

    Remarks: This constructor shall not participate in overload resolution unless each type in UTypes is implicitly convertible to its corresponding type in Types.

    14 [Note: enable_if can be used to make the converting constructor and assignment operator exist only in the cases where the source and target have the same number of elements. — end note]

  5. Following [tuple.cnstr]/16 add a new Remarks element:

    template <class... UTypes> tuple(tuple<UTypes...>&& u);
    

    15 Requires: Each type in Types shall shall satisfy the requirements of MoveConstructible (Table 33) from the corresponding type in UTypes. sizeof...(Types) == sizeof...(UTypes).

    16 Effects: Move-constructs each element of *this with the corresponding element of u.

    Remarks: This constructor shall not participate in overload resolution unless each type in UTypes is implicitly convertible to its corresponding type in Types.

    [Note: enable_if can be used to make the converting constructor and assignment operator exist only in the cases where the source and target have the same number of elements. — end note]

  6. Following [tuple.cnstr]/18 add a new Remarks element:

    template <class U1, class U2> tuple(const pair<U1, U2>& u);
    

    17 Requires: The first type in Types shall be constructible from U1 and the second type in Types shall be constructible from U2. sizeof...(Types) == 2.

    18 Effects: Constructs the first element with u.first and the second element with u.second.

    Remarks: This constructor shall not participate in overload resolution unless U1 is implicitly convertible to the first type in Types and U2 is implicitly convertible to the second type in Types.

  7. Following [tuple.cnstr]/20 add a new Remarks element:

    template <class U1, class U2> tuple(pair<U1, U2>&& u);
    

    19 Requires: The first type in Types shall shall satisfy the requirements of MoveConstructible(Table 33) from U1 and the second type in Types shall be move-constructible from U2. sizeof...(Types) == 2.

    20 Effects: Constructs the first element with std::move(u.first) and the second element with std::move(u.second)

    Remarks: This constructor shall not participate in overload resolution unless U1 is implicitly convertible to the first type in Types and U2 is implicitly convertible to the second type in Types.

Date: 2010-03-20.00:00:00

In analogy to library defect 811, tuple's variadic constructor

template <class... UTypes>
explicit tuple(UTypes&&... u);

creates the same problem as pair:

#include <tuple>

int main()
{
  std::tuple<char*> p(0);
}

produces a similar compile error for a recent gcc implementation.

I suggest to follow the same resolution path as has been applied to pair's corresponding c'tor, that is require that these c'tors should not participate in overload resolution, if the arguments are not implicitly convertible to the element types.

Further-on both pair and tuple provide converting constructors from different pairs/tuples that should be not available, if the corresponding element types are not implicitly convertible. It seems astonishing that in the following example

struct A {
  explicit A(int);
};

A  a = 1; // Error

std::tuple<A> ta = std::make_tuple(1); // # OK?

the initialization marked with # could be well-formed.

History
Date User Action Args
2010-11-18 14:01:09adminsetstatus: nad editorial -> resolved
2010-11-11 03:10:11adminsetmessages: + msg5328
2010-11-11 03:10:11adminsetstatus: new -> nad editorial
2010-10-24 22:38:51adminsetmessages: + msg5084
2010-10-21 18:28:33adminsetmessages: + msg1600
2010-03-20 00:00:00admincreate