This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115e. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.
2024-11-11
Moving to always doing overload resolution for determining exception specifications and implicit deletion creates some unfortunate cycles:
template<typename T> struct A { T t; }; template <typename T> struct B { typename T::U u; }; template <typename T> struct C { C(const T&); }; template <typename T> struct D { C<B<T> > v; }; struct E { typedef A<D<E> > U; }; extern A<D<E> > a; A<D<E> > a2(a);
If declaring the copy constructor for A<D<E>> is part of instantiating the class, then we need to do overload resolution on D<E>, and thus C<B<E>>. We consider C(const B<E>&), and therefore look to see if there's a conversion from C<B<E>> to B<E>, which instantiates B<E>, which fails because it has a field of type A<D<E>> which is already being instantiated.
Even if we wait until A<D<E>> is considered complete before finalizing the copy constructor declaration, declaring the copy constructor for B<E> will want to look at the copy constructor for A<D<E>>, so we still have the cycle.
I think that to avoid this cycle we need to short-circuit consideration of C(const T&) somehow. But I don't see how we can do that without breaking
struct F { F(F&); }; struct G; struct G2 { G2(const G&); }; struct G { G(G&&); G(const G2&); }; struct H: F, G { }; extern H h; H h2(h);
Here, since G's move constructor suppresses the implicit copy constructor, the defaulted H copy constructor calls G(const G2&) instead. If the move constructor did not suppress the implicit copy constructor, I believe the implicit copy constructor would always be viable, and therefore a better match than a constructor taking a reference to another type.
So perhaps the answer is to reconsider that suppression and then disqualify any constructor taking (a reference to) a type other than the constructor's class from consideration when looking up a subobject constructor in an implicitly defined constructor. (Or assignment operator, presumably.)
Another possibility would be that when we're looking for a conversion from C<B<E>> to B<E> we could somehow avoid considering, or even declaring, the B<E> copy constructor. But that seems a bit dodgy.
Additional note (October, 2010):
An explicitly declared move constructor/op= should not suppress the implicitly declared copy constructor/op=; it should cause it to be deleted instead. This should prevent a member function taking a (reference to) an un-reference-related type from being chosen by overload resolution in a defaulted member function.
And we should clarify that member functions taking un-reference-related types are not even considered during overload resolution in a defaulted member function, to avoid requiring their parameter types to be complete.