The following issue was discussed in the libstdc++ development list
with Peter Dimov and Paolo Carlini while discussing the initialization
of tuple objects with "move-only" types.
Consider the following code.
template <typename Element>
struct tuple {
tuple () { }
template <typename UElements>
explicit
tuple (UElements&& elements)
{ static_assert(!sizeof(UElements), "first constructor"); }
template <typename UElements>
tuple (tuple<UElements>&& foo)
{ static_assert(!sizeof(UElements), "second constructor"); }
};
int
main (int argc, char* argv[]) {
tuple<int> f1;
tuple<unsigned> f2(f1); // A
return 0;
}
Line A above will always fails with the message "second constructor".
The overload set for constructing f2 is deduced as:
tuple( tuple<int>& elements ); // first constructor
tuple( tuple<int>&& foo ); // second constructor
with the first constructor a better match for the lvalue f1.
The obvious intent is for tuple objects to select the second
constructor.
The proposed solution was to introduce a third constructor to better
match lvalue tuples.
template <typename UElements>
tuple (tuple<UElements>& foo)
{ static_assert(!sizeof(UElements), "third constructor"); }
Is this solution complete?
--
P.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
It fails if:
const tuple<int> f1;
which now binds back to "first constructor".
What we really need to do is constrain:
template <typename ...UElements>
explicit
tuple (UElements&&... elements)
such that tuple is constructible with UElements... . Currently the best
option for that is using concepts and std::Constructible (not yet in the
WP).
Failing that, the following is looking like a decent strategy to me:
template <typename ...Types>
struct tuple {
tuple () { }
explicit tuple(Types...)
{ static_assert(!sizeof(Types), "first constructor"); }
template <typename ...UElements>
tuple (tuple<UElements...>&& foo)
{ static_assert(!sizeof...(UElements), "second constructor"); }
template <typename ...UElements>
tuple (const tuple<UElements...>& foo)
{ static_assert(!sizeof...(UElements), "third constructor"); }
};
The implementation would move from the by value arguments in first
constructor.
-Howard