Addressed issues:
LWG 2312 ispartially addressed: The currently proposed wording still intends to make an implementation conforming that would accept a smaller number of elements values compared to theactual tuple-size (and to value-initialize the remaining ones), therefore the size-related requirements are currently not part of theSFINAE constraints. If LWG 2312 would be accepted the corresponding constraints would be similarly adjusted as described by the issue and the corresponding requirement could be removed.
Changes sinceN4064 (revision 2):
Improve and simplify definition ofEXPLICIT as well as the individual member specifications by separating out the number of constructor declarations from the decision to addexplicit or not.
After the Cologne meeting the authors recognized that three of the existingtuple allocator constructors haven beenincorrectly marked withEXPLICIT. The constructors
template <class Alloc> tuple(allocator_arg_t, const Alloc& a);template <class Alloc> tuple(allocator_arg_t, const Alloc& a, const tuple&);template <class Alloc> tuple(allocator_arg_t, const Alloc& a, tuple&&);
correspond to the three non-allocator constructors
constexpr tuple();tuple(const tuple&) = default;tuple(tuple&&) = default;
and none of these can beexplicit as declared (The core language does not propagateexplicit forspecial members when declared in that form). The whole idea of propagatingEXPLICIT to theallocator constructors was to reflect theexplicit-nature of thecorresponding non-tuple constructors. Torealize this,EXPLICIT need to be removed from the three aforementioned constructors.
Changes sinceN3739 (revision 1):
Improved the current rationale (Thanks to Mike Spertus!)
During the Rapperswil LEWG discussion a new vote lead to a revised result on the preferredrepresentation form:
Do we prefer theEXPLICIT(see below) constexpr tuple(UTypes&&...); editorial representation?
SF-F-N-A-SA0-8-3-2-0
Therefore this revision now uses the annotationEXPLICIT for the affected constructor pairsand defines the meaning of that in Clause 17.
Changes sinceN3680:
During a straw-pool vote there was a small majority (4:3) in favour for keeping the two-signatures approach,so this presentation form was used in this revision.
The original example has been improved.
The missing application of the declaration pattern to theallocator_arg_t constructors has been fixed.
Consequent usage of the more preciseis_convertible trait instead of the somewhat vagueis implicitly convertible description.
For many programmers it is a surprise to find out, that the following code is rejected by their compiler:
std::tuple<int, int> pixel_coordinates() { return {10, -15};// Oops: Error}struct NonCopyable { NonCopyable(int); NonCopyable(const NonCopyable&) = delete; };std::pair<NonCopyable, double> pmd{42, 3.14};// Oops: ErrorWhat is wrong with this? Why doesn't this just work?
This paper explains the reason for the current specification ofpair andtuple and suggestschanges to the working paper to fix these and some other bothersome constraints of these general type wrappers.At the time whenN3240 wasproposed, several driving forces defined the constraints of resolving a bunch of library issues and NB comments.
One notable intention was to prevent that the type wrapperstuple andpair should allowimplicit conversions for wrapped types that are not implicitly convertible, as expressed byLWG 1324 andDE 15.Another relevant requirement was to keep backward-compatibility to C++03 in regard to null pointer literalsexpressed as integral null constants as described byLWG 811.At that time there was a strong resistance to add further constructors especially tostd::pair. At some point intime there did exist a very large number of such constructors due to allocator support. One important consequence of this pair simplification was the acceptance ofN3059.Thus with the previous acceptance of proposalN3240the specification provides the followingadvantages forpair andtuple:Heterogenous template constructors are constrained upon the criterion that the element-wise source types areimplicitly convertible to the element-wise destination types.
struct B { explicit B(bool); };std::tuple<B> tb = std::tuple<bool>();// ErrorThe non-template constructortuple(const Types&...) and the corresponding template-constructor areexplicit. This prevents that a single-element tuple from beingcopy-initialized by an argument object and has only anexplicit constructor for this construction.
struct X { X(); explicit X(const X&); } x;std::tuple<X> tx = x;// Errorstruct E { explicit E(int); };std::tuple<E> te = 42;// ErrorNon-template constructors accepting a sequence of elements, such asexplicit tuple(const Types&...)andpair(const T1& x, const T2& y), are still kept to support the special conversion scenario where a pointer(-to-member) element type is initialized with thenull pointer constant0 instead ofnullptr.
class C;std::tuple<int*> tpi(0);// OKstd::tuple<int C::*> tpmi(0);// OK
We propose thatpair/tuple obey the same initialization rules as their underlying element types. Unless we have such "perfect initialization",pair andtuple exhibit confusing and unintuitive behavior as illustrated by the examples below.
std::tuple<int, int> foo_tuple() { return {1, -1};// Error}std::pair<int, int> foo_pair() { return {1, -1};// OK}struct D { D(int); D(const D&) = delete; };std::tuple<D> td(12);// Errorstruct Y { explicit Y(int); };std::tuple<Y> ty(12);// ErrorIt has been observed byJohannes Schaub that there exists a defect withtuple in regard to the non-template constructorexplicit tuple(const Types&...): The current specification has the effect thatthe instantiation oftuple<> would be required to be ill-formed because it has two conflicting default constructors.
Starting with the last point: This is indeed a simple oversight that slipped in during thetuple standardization. TheTR1 document did have the following specification:
template <class T1 =unspecified , class T2 =unspecified , ..., class TM =unspecified >class tuple{public: tuple(); explicit tuple(P1, P2, ..., PN);// iff N > 0 […]};When the variadic form of tuples was proposed viaN2151and its successors, the highlighted size constraint inadvertently got lost.
The other three problems are all caused by (A) constructors that are alwaysexplicit and (B) by constrained constructortemplates that imposeimplicit convertible constraints on the element types.
So, notwithstanding the good motivation behind the current specification ofpair andtuple, it turns out to havesome unfortunate consequences.
This proposal is intending to solve all these problems by a simple procedure that still ensures that all positive aspects of the currentspecification are conserved.
Before explaining the general outline of this proposal it is more helpful to start with a simple, but useful programming idiom.
Consider the following class templateA that is intended to be used as a wrapper for some other typeT:#include <type_traits>#include <utility>template<class T>struct A { template<class U, typename std::enable_if<std::is_constructible<T, U>::value &&std::is_convertible<U, T>::value , bool>::type = false > A(U&& u) : t(std::forward<U>(u)) {} template<class U, typename std::enable_if<std::is_constructible<T, U>::value &&!std::is_convertible<U, T>::value , bool>::type = false > explicit A(U&& u) : t(std::forward<U>(u)) {} T t;};The shown constructors both useperfect forwardingand they have essentially the same signatures except for one being explicit, the other one not. Furthermore, they aremutually exclusively constrained. In other words: This combination behaves for any destination typeT and any argument typeU like asingle constructor that iseither explicit or non-explicit (or no constructorat all). Attempts to construct aA<T> from some value of typeU will reflect the allowed initialization forms of the wrapped typeT:
struct Im{ Im(int){} };struct Ex{ explicit Ex(int){} };A<Im> ai1(1); // OKA<Im> ai2{2}; // OKA<Im> ai3 = 3; // OKA<Im> ai4 = {4}; // OKA<Ex> ae1(1); // OKA<Ex> ae2{2}; // OKA<Ex> ae3 = 3; // ErrorA<Ex> ae4 = {4}; // ErrorThis technique can easily be extended to the variadic template case, and when doing so can be considered as a key to solvingthe problems oftuple andpair.
It should be noted here, that for the general case thestd::is_constructible<T, U>::value requirement for thenon-explicit constructor which is constrained onstd::is_convertible<U, T>::value is not redundant, because itis possible to create types that can be copy-initialized but not direct-initialized:struct Odd { explicit Odd(int) = delete; Odd(long){}};Odd o2 = 1; // OKOdd o1(1); // ErrorTechnically it would be possible to apply the same technique of creating element-dependent explicit or non-explicit defaultconstructors. This application was shortly considered during the write-up of this proposal, but rejected because for the current C++ rules there is no longer any observable difference for anexplicit default constructor that cannot be invoked with more than zero arguments and one that is notexplicit.
The technique cannot be applied to copy/move operators in the same way as for the other constructors (because these specialmember functions cannot be templates) and given the very rare request for such a support the idea was no further investigated by the author. A second argument against providing this support is based on the consistency with the core language rulesthat theexplicit-character of these constructors is not conserved for the implicitly declared versions in classes that contain corresponding sub-objects with such explicit constructors.As shown above, the current over-constraining restrictions of thepair andtuple constructors are due tounconditional usage ofexplicit andimplicitly convertible requirements.
The general approach of this proposal is to require "perfect initialization" semantics forpair andtuple constructors extended to the variadic case. Albeit this seemingly doubles the number of constructor declarations in the draft, it does not change theeffective number of these for a particular combination of element type and source type of some initialization due to their mutual exclusion property.In theory the same technique could be applied to thepiecewise_construct_t ofpair. This proposal does not propose this, because this constructor is specifically asked for by the corresponding tag and there are no furtherconstraint except theis_constructible requirements.In addition, this proposal fixes the specification problem oftuple<>'s default constructors.The wording is intentionally chosen, so that an implementation is not required (but allowed) to use the "perfect initialization" idiom.This is done by taking advantage of the already existing nomenclature "This function does not participate in overload resolution unless […]". Its worth emphasizing that even though this phrase is usually used to describe constrained templates in the Library specification, the actual wording of this doesn't necessarily imply to "sfinae out" template functions. Many library implementations solve this problem by providing a specialization for the emptytuple case that does not provide the additional default constructor, for example. This is also a valid way to ensure that functions don't participate in overload resolution.In C++03explicit constructors had no behavioural difference, unless they had been single-argument constructors, soone might suggest to restrict adding theexplicit keyword to constructors that take exactly one argument.
I think this is idea is flawed (unless I'm using specifically tagged constructors like the piecewise-one ofpair). Consider the following example:#include <tuple>#include <chrono>#include <iostream>using hms_t = std::tuple<std::chrono::hours, std::chrono::minutes, std::chrono::seconds>;void launch_rocket_at(std::chrono::seconds s){ std::cout << "launching rocket in " << s.count() << " seconds!\n";}void launch_rocket_at(hms_t times){ using namespace std; launch_rocket_at(get<0>(times) + get<1>(times) + get<2>(times));}int main(){ using namespace std;launch_rocket_at(make_tuple(1, 2, 3)); // #1: very scarylaunch_rocket_at({1, 2, 3}); // #2: even scarier using namespace std::chrono;launch_rocket_at(make_tuple(hours(1), minutes(2), seconds(3))); // #3: Perfectly clear!launch_rocket_at({hours(1), minutes(2), seconds(3)}); // #4: Also clear!launch_rocket_at(hms_t{1, 2, 3}); // #5: And this, too}which should output:
launching rocket in 3723 seconds!launching rocket in 3723 seconds!launching rocket in 3723 seconds!
If the former two calls to functionlaunch_rocket_at where possible, this would directly subvert theintended explicitness of thestd::duration constructor and would make using the time-utilitytypes much more unsafe. Why? Consider the following scenario:
If the client believed that the order of the units wasseconds, minutes, hours, and input 3, 2, 1, — intending 3 seconds, 2 minutes, and 1 hour — the rocket would launch in 10,921 seconds instead of the intended 3,723 seconds. This mistake can indeed easily happen, if you look again at the lines marked with#1 and#2.Due to our intentionally conserved constraints to be explicit here we catch that mistake at compile-time, instead of having to shoot the rocket down...During the write-up of this proposal I had the idea of replacing the prototype declaration pairs bya single one expressed by some pseudo-macro that looks like a single declaration. For example
template <class... UTypes>constexpr tuple(UTypes&&...);template <class... UTypes>explicit constexpr tuple(UTypes&&...);
could instead be declared as follows:
template <class... UTypes>EXPLICIT constexpr tuple(UTypes&&...);
This form of representation still means thatEXPLICIT needs to be defined somewhereand somehow, but only once.
There was a strong preference during the Rapperswil 2014 LEWG discussion to use the "macro" instead of the individual declarations.This paper does not use a previously suggested form ofEXPLICIT(see below) instead ofEXPLICIT, because there is nothing specific to explain below again (The different SFINAE conditions are not relevant at that point,because they appear as usual with the normal, detailed prototype specifications).In any way, the final editorial decision is being handed over to the project editor.The proposed wording changes refer toN4296.
Add an additional new paragraph at the end of 17.5.2.2 [functions.within.classes] and move the current second paragraph to the end of the firstparagraph:
-1- For the sake of exposition, Clauses 18 through 30 and Annex D do not describe copy/move constructors,assignment operators, or (non-virtual) destructors with the same apparent semantics as those that can begenerated by default (12.1, 12.4, 12.8).It is unspecified whether the implementation provides explicit definitions for such member function signatures, or for virtual destructors that can be generated by default.
-2- It is unspecified whether the implementation provides explicit definitions for such member function signatures,or for virtual destructors that can be generated by default.-?- For the sake of exposition, the library clauses sometimes annotate constructors withEXPLICIT. Such a constructor is conditionally declared as either explicit or non-explicit (12.3.1 [class.conv.ctor]). [Note: This is typically implemented by declaring two such constructors, of which at most one participates in overload resolution —end note]
Change 20.3.2 [pairs.pair], class templatepair synopsis, as indicated:
namespace std { 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; constexpr pair();EXPLICIT constexpr pair(const T1& x, const T2& y); template<class U, class V>EXPLICIT constexpr pair(U&& x, V&& y); template<class U, class V>EXPLICIT constexpr pair(const pair<U, V>& p); template<class U, class V>EXPLICIT constexpr pair(pair<U, V>&& p); template <class... Args1, class... Args2> pair(piecewise_construct_t, tuple<Args1...> first_args, tuple<Args2...> second_args); pair& operator=(const pair& p); template<class U, class V> pair& operator=(const pair<U, V>& p); pair& operator=(pair&& p) noexcept(see below); template<class U, class V> pair& operator=(pair<U, V>&& p); void swap(pair& p) noexcept(see below); };}Change 20.3.2 [pairs.pair] around p5 as indicated:
EXPLICIT constexpr pair(const T1& x, const T2& y);-6-Effects: The constructor initializesfirst withx andsecond withy.-?-Remarks: This constructor shall not participate in overload resolution unlessis_copy_constructible<first_type>::value istrue andis_copy_constructible<second_type>::value istrue. The constructor is explicit if and only ifis_convertible<const first_type&, first_type>::value isfalse oris_convertible<const second_type&, second_type>::value isfalse.
-5-Requires:is_copy_constructible<first_type>::value istrue andis_copy_constructible<second_type>::value istrue.
Change 20.3.2 [pairs.pair] around p7 as indicated:
template<class U, class V>EXPLICIT constexpr pair(U&& x, V&& y);-8-Effects: The constructor initializesfirst withstd::forward<U>(x) andsecond withstd::forward<V>(y).-9-Remarks:
-7-Requires:is_constructible<first_type, U&&>::value istrue andis_constructible<second_type, V&&>::value istrue.IfU is not implicitly convertible tofirst_type orV is not implicitly convertible tosecond_type this constructor shall not participate in overload resolution.This constructor shall not participate in overload resolution unlessis_constructible<first_type, U&&>::value istrue andis_constructible<second_type, V&&>::value istrue. The constructor is explicit if and only ifis_convertible<U&&, first_type>::value isfalse oris_convertible<V&&, second_type>::value isfalse.
Change 20.3.2 [pairs.pair] around p10 as indicated:
template<class U, class V>EXPLICIT constexpr pair(const pair<U, V>& p);-11-Effects:The constructor initializes
-10-Requires:is_constructible<first_type, const U&>::value istrue andis_constructible<second_type, const V&>::value istrue.Initializesmembers from the corresponding members of the argument.-12-Remarks: This constructor shall not participate in overload resolution unlessconst U& is implicitly convertible tofirst_type andconst V& is implicitly convertible tosecond_typeis_constructible<first_type, const U&>::value istrue andis_constructible<second_type, const V&>::value istrue. The constructor is explicit if and only ifis_convertible<const U&, first_type>::value isfalse oris_convertible<const V&, second_type>::value isfalse.
Change 20.3.2 [pairs.pair] around p13 as indicated:
template<class U, class V>EXPLICIT constexpr pair(pair<U, V>&& p);-14-Effects: The constructor initializesfirst withstd::forward<U>(p.first) andsecond withstd::forward<V>(p.second).-15-Remarks: This constructor shall not participate in overload resolution unless
-13-Requires:is_constructible<first_type, U&&>::value istrue andis_constructible<second_type, V&&>::value istrue.U is implicitly convertible tofirst_type andV is implicitly convertible tosecond_typeis_constructible<first_type, U&&>::value istrue andis_constructible<second_type, V&&>::value istrue. The constructor is explicit if and only ifis_convertible<U&&, first_type>::value isfalse oris_convertible<V&&, second_type>::value isfalse.
Change 20.4.2 [tuple.tuple], class templatetuple synopsis, as indicated. The intent is to declarethe set of"conditionally explicit" constructors and to fix themultiple default constructorproblem for empty tuples.
namespace std { template <class... Types> class tuple { public: // 20.4.2.1, tupleconstruction constexpr tuple();EXPLICIT constexprexplicit tuple(const Types&...);// only if sizeof...(Types) >= 1 template <class... UTypes>EXPLICIT constexprexplicit tuple(UTypes&&...);// only if sizeof...(Types) >= 1 tuple(const tuple&) = default; tuple(tuple&&) = default; template <class... UTypes>EXPLICIT constexpr tuple(const tuple<UTypes...>&); template <class... UTypes>EXPLICIT constexpr tuple(tuple<UTypes...>&&); template <class U1, class U2>EXPLICIT constexpr tuple(const pair<U1, U2>&);// only if sizeof...(Types) == 2 template <class U1, class U2>EXPLICIT constexpr tuple(pair<U1, U2>&&);// only if sizeof...(Types) == 2// allocator-extended constructors template <class Alloc> tuple(allocator_arg_t, const Alloc& a); template <class Alloc>EXPLICIT tuple(allocator_arg_t, const Alloc& a, const Types&...); template <class Alloc, class... UTypes>EXPLICIT tuple(allocator_arg_t, const Alloc& a, UTypes&&...); template <class Alloc> tuple(allocator_arg_t, const Alloc& a, const tuple&); template <class Alloc> tuple(allocator_arg_t, const Alloc& a, tuple&&); template <class Alloc, class... UTypes>EXPLICIT tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&); template <class Alloc, class... UTypes>EXPLICIT tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&&); template <class Alloc, class U1, class U2>EXPLICIT tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&); template <class Alloc, class U1, class U2>EXPLICIT tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&&); [..] };}Change 20.4.2.1 [tuple.cnstr] around p6 as indicated:
EXPLICIT constexprexplicittuple(const Types&...);-7-Effects:The constructor initializes
-6-Requires:is_copy_constructible<Ti>::value is true for alli.Initializeseach element with the value of the corresponding parameter.-?-Remarks: This constructor shall not participate in overload resolution unlesssizeof...(Types) >= 1 andis_copy_constructible<Ti>::value istrue for alli. The constructor is explicit if and only ifis_convertible<constTi&,Ti>::value isfalse for at least onei.
Change 20.4.2.1 [tuple.cnstr] around p8 as indicated:
template <class... UTypes>EXPLICIT constexprexplicittuple(UTypes&&... u);-8-Requires:sizeof...(Types) == sizeof...(UTypes).
-9-Effects:The constructor initializesis_constructible<Ti,Ui&&>::value is true for alli.Initializesthe elements in the tuple with the corresponding value instd::forward<UTypes>(u).-10-Remarks: This constructor shall not participate in overload resolution unlesseach type inUTypes is implicitly convertible to its corresponding type inTypessizeof...(Types) >= 1 andis_constructible<Ti,Ui&&>::value istrue for alli. The constructor is explicit if and only ifis_convertible<Ui&&,Ti>::value isfalse for at least onei.
Change 20.4.2.1 [tuple.cnstr] around p15 as indicated:
template <class... UTypes>EXPLICIT constexpr tuple(const tuple<UTypes...>& u);-15-Requires:sizeof...(Types) == sizeof...(UTypes).
-16-Effects:The constructor initializesis_constructible<Ti, constUi&>::value is true for alli.Constructseach element of*this with the corresponding element ofu.-17-Remarks: This constructor shall not participate in overload resolution unlessconstUi& is implicitly convertible toTi for alliis_constructible<Ti, constUi&>::value istrue for alli. The constructor is explicit if and only ifis_convertible<constUi&,Ti>::value isfalse for at least onei.
Change 20.4.2.1 [tuple.cnstr] around p18 as indicated:
template <class... UTypes>EXPLICIT constexpr tuple(tuple<UTypes...>&& u);-18-Requires:sizeof...(Types) == sizeof...(UTypes).
-19-Effects: For alli,the constructor initializes theith element of*this withstd::forward<Ui>(get<i>(u)).-20-Remarks: This constructor shall not participate in overload resolution unlessis_constructible<Ti,Ui&&>::value is true for alli.each type inUTypes is implicitly convertible to its corresponding type inTypesis_constructible<Ti,Ui&&>::value istrue for alli. The constructor is explicit if and only ifis_convertible<Ui&&,Ti>::value isfalse for at least onei.
Change 20.4.2.1 [tuple.cnstr] around p21 as indicated:
template <class U1, class U2>EXPLICIT constexpr tuple(const pair<U1, U2>& u);-21-Requires:sizeof...(Types) == 2.
-22-Effects:The constructor initializesis_constructible<T0, const U1&>::value is true for the first typeT0 inTypes andis_constructible<T1, const U2&>::value is true for the second typeT1 inTypes.Constructsthe first element withu.first and the second element withu.second.-23-Remarks: This constructor shall not participate in overload resolution unlessconst U1& is implicitly convertible toT0 andconst U2& is implicitly convertible toT1is_constructible<T0, const U1&>::value istrue andis_constructible<T1, const U2&>::value istrue. The constructor is explicit if and only ifis_convertible<const U1&,T0>::value isfalse oris_convertible<const U2&,T1>::value isfalse.
Change 20.4.2.1 [tuple.cnstr] around p24 as indicated:
template <class U1, class U2>EXPLICIT constexpr tuple(pair<U1, U2>&& u);-24-Requires:sizeof...(Types) == 2.
-25-Effects:The constructor iis_constructible<T0, U1&&>::value is true for the first typeT0 inTypes andis_constructible<T1, U2&&>::value is true for the second typeT1 inTypes.Initializes the first element withstd::forward<U1>(u.first) and the second element withstd::forward<U2>(u.second).-26-Remarks: This constructor shall not participate in overload resolution unlessU1 is implicitly convertible toT0 andU2 is implicitly convertible toT1is_constructible<T0, U1&&>::value istrue andis_constructible<T1, U2&&>::value istrue. The constructor is explicit if and only ifis_convertible<U1&&,T0>::value isfalse oris_convertible<U2&&,T1>::value isfalse.
Change 20.4.2.1 [tuple.cnstr] around p27 as indicated:
template <class Alloc> tuple(allocator_arg_t, const Alloc& a);template <class Alloc>EXPLICIT tuple(allocator_arg_t, const Alloc& a, const Types&...);template <class Alloc, class... UTypes>EXPLICIT tuple(allocator_arg_t, const Alloc& a, UTypes&&...);template <class Alloc> tuple(allocator_arg_t, const Alloc& a, const tuple&);template <class Alloc> tuple(allocator_arg_t, const Alloc& a, tuple&&);template <class Alloc, class... UTypes>EXPLICIT tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&);template <class Alloc, class... UTypes>EXPLICIT tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&&);template <class Alloc, class U1, class U2>EXPLICIT tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&);template <class Alloc, class U1, class U2>EXPLICIT tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&&);-27-Requires:Alloc shall meet the requirements for anAllocator (17.6.3.5).
-28-Effects: Equivalent to the preceding constructors except that each element is constructed with uses-allocatorconstruction (20.7.7.2).
The following example presents how to constrain even non-template functions such as the constructorsthat directly take the element types.
template<class T1, class T2>struct pair { […] template<class U1 = T1, class U2 = T2, typename enable_if< is_copy_constructible<U1>::value && is_copy_constructible<U2>::value && is_convertible<const U1&, U1>::value && is_convertible<const U2&, U2>::value , bool>::type = false > constexpr pair(const T1&, const T2&); template<class U1 = T1, class U2 = T2, typename enable_if< is_copy_constructible<U1>::value && is_copy_constructible<U2>::value && !(is_convertible<const U1&, U1>::value && is_convertible<const U2&, U2>::value) , bool>::type = false > explicit constexpr pair(const T1&, const T2&);};I would like to thank Howard Hinnant for his very helpful discussions and comments during reviews of this paper and forhis motivating example. Thanks also to Jonathan Wakely for his review that improved this proposal to a large extend.Thanks as well go to Mike Spertus for helping to improve the rationale.