I have a class hierarchy with three classes (A, B and C). A and B are base-classes, parametrized with the derived Type. Class C is derived from both, A and B.
The class B provides an assignment operator for objects of type A and class C inherits this assignment operator with the using super::operator=
declaration.
When I define a constructor in class B from objects of type A, I get the Error: two overloads have similar conversions (C2666) in Visual Studio 2013, but I don't get any error, or warning in gcc (4.8.2), clang (3.4) and intel icc (Studio 2015). (compiled with -Wall -pedantic
)
Here the reduced example:
template <class Model> struct A {};
template <class Model> struct B
{
B() {}; // default constructor
// copy constructor for objects of type A
template <class M>
B(A<M> const&) {}
// assignment operator for objects of type A
template <class M>
Model& operator=(A<M> const& rhs)
{
return static_cast<Model&>(*this);
}
};
struct C : public B<C>, public A<C>
{
typedef B<C> super;
// copy assignment operator
C& operator=(C const& rhs) { return *this; }
// adopt assignment operator for A<C> from super-class
using super::operator=;
};
int main()
{
C c;
A<C> a;
c = a;
}
If I would replace the templated class A by a non-templated class it also compiles in Visual Studio without errors - but this is not the way it could be solved.
My question is: is this construct well-formed in the sense that it is standard conform, or is the error-message correct? Does a specifier like explicit
for the copy constructor in B helps to solve the problem?
By the way: In Visual Studio, I get the Warning: multiple assignment operators specified (C4522), because of the copy assignment operator in class C. Can somebody exmplain to me, why this should be a problem?
GCC and CLANG are correct and MSVC is wrong:
The statement c=a;
uses the operator=
that you've defined in B
, because an A<C>
is not necessarily a C
. So let's write down the the declaration of operator=
of B<C>
by manually doing the type substitution:
template <class M>
C& operator=(A<M> const& rhs)
As a
is a A<C>
, the obvious implicit instantiation candidate of this template would be:
C& operator=(A<C> const& rhs)
This is in fact the only possible instantiation (you could verify that GCC uses it by displaying the typeinfo).
If you'd change simplify the class C to an even more minimalistic form, you'd still get theerror:
struct C : public B<C> // single inheritance
{ using B<C>::operator=; }; // nothing else
In fact the problem is caused by the constructor B(A<M> const&)
:
MSVC identifies wrongly a second potental candidate for the member function's implicit specialisation. As this constructor allows to convert implicitely from A<M>
to B<C>
, the candidate is:
C& operator=(B<C> const& rhs)
But according to the C++ standard, this shouldn't be envisaged by the compiler at all:
14.8.1/6: Implicit conversions will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter type contains no template-parameters that participate in template argument deduction.
So this is clearly a bug of MSVC.
By the way:
The warning about multiple assignemnt operators is just an information. Apparently MS assumes that this might be a frequent cause for mistakes. And now to the core question...