I was pretty sure that the answer to that question was, "Never, ever can a template be the copy constructor."
Unfortunately, I just spent 3 hours figuring out why I was getting a warning about recursion, tracked it to the copy constructor, watched the debugger go insane and not let me look at the recursive code, and finally tracked it down to a missing '&' in a base constructor.
You see, I have this complex policy-based design host that's been working fine for a while now. I went about overriding two policies in one and ran into a recursive copy constructor. Narrowed it down to one policy that is required to provide a constructor that can take a type of XXX concept as its argument, but in this case I'm just discarding it. So I wrote
struct my_policy
{
template < typename T >
my_polity(T const) {} // missing '&'...oops
};
Now, my_policy is a base class to the host (of course) and this little typo caused recursion where the host's copy constructor passed itself up the chain to this, templated constructor rather than an implicit, compiler generated copy constructor. It would then of course call its copy constructor again to create the temporary.
The truly fascinating thing is that I can't recreate this in simplified code. Even with a sort of mock policy host example I can't make it happen. The following code does not exhibit the issue:
#include <boost/utility/enable_if.hpp>
#include <boost/mpl/bool.hpp>
struct base
{
template < typename T >
base(T const) {}
};
struct another_base
{
int x;
another_base(int y) : x(y) {}
};
template < typename T >
struct is_derived : boost::mpl::false_ {};
template < typename T1, typename T2 >
struct derived : T1, T2
{
template < typename T >
derived(T const& x, typename boost::disable_if< is_derived<T> >::type * = 0) : T1(0), T2(x) {}
};
template < typename T1, typename T2 >
struct is_derived<derived<T1,T2>> : boost::mpl::true_ {};
int main()
{
derived<base, another_base> d(23);
derived<base, another_base> x = d;
}
I am using boost's parameter library to make the 7 or so arguments to the host accessible by "name". Maybe that's the issue, I don't know. At any rate, I'm wondering if someone out there knows what specific conditions, if any, could cause a compiler to legitimately use the templated constructor for "base" as a copy constructor or from the implicit copy constructor for "derived".
Edit note:
I recreated the problem in the above code by giving "another_base" an explicit copy constructor:
struct another_base
{
int x;
another_base(another_base const& b) : x(b.x) {}
another_base(int y) : x(y) {}
};
Starting to conclude that this is a compiler bug unless someone can tell me why this is legitimate.
More information:
struct derived;
struct base
{
base() {}
private:
base(derived const&);
};
struct base2
{
base2() {}
//base2 (base2 const&) {}
};
struct derived : base, base2 {};
int main()
{
derived d1; derived d2(d1);
}
Looking more at Schaub's answer I took the above code and compiled it. It compiles just fine until you uncomment base2's copy constructor declaration. Then it will blow up in the way I'm assuming was expected with the original code (no access to private constructor in base). So templates aren't even part of the issue; you can recreate the problem without them. Looks like it's an MI issue, which VS has always been a little slow at getting right.
I've changed the tags to reflect this finding.
Posted to MS's bug repository
I included a work around in the example code.