Reputation: 1946
Is it possible to define a constructor for all derived types and a template constructor? I've written this testcase to illustrate my problem:
#include <iostream>
class Variant;
class CustomVariant;
class Variant
{
public:
Variant(void)
{}
Variant(const Variant&)
{
std::cout << "ctor" << std::endl;
}
Variant(const CustomVariant&)
{
std::cout << "custom" << std::endl;
}
template<typename T>
Variant(const T&)
{
std::cout << "template" << std::endl;
}
};
class CustomVariant : public Variant
{
};
class DerivedVariantA : public CustomVariant
{
};
class DerivedVariantB : public CustomVariant
{
};
int main(void)
{
DerivedVariantB dvb;
Variant v(dvb);
// expcected output: "custom" instead of "template"
}
Upvotes: 4
Views: 448
Reputation: 355167
template <typename T> Variant(const T&) // (a)
Variant(const CustomVariant&) // (b)
No conversion is required to call (a); the argument type, DerivedVariantB
, is an exact match where T = DerivedVariantB
.
A derived-to-base conversion is required to call (b). Therefore, (a) is a better match than (b).
If you invoke the constructor with an argument of type CustomVariant
, both constructors are exact matches, so (b) is selected because where everything else is equal, a nontemplate is preferred over a template.
You can suppress use of the template where T
is derived from Variant
by using std::enable_if
:
template<typename T>
Variant(const T&,
typename std::enable_if<
!std::is_base_of<Variant, T>::value, void*
>::type = 0)
{
std::cout << "template" << std::endl;
}
This makes the template not instantiable when T
is derived from Variant
, so it will not be available during overload resolution. enable_if
and is_base_of
are new to C++ in C++0x and your compiler and standard library may support them. If not, you can also find them in C++ TR1 or Boost.TypeTraits.
Upvotes: 6
Reputation: 34625
No, among the list of constructors available in the class there is no constructor that takes the instance of type DerivedVariantB
as the argument. Hence the template generated is being called.
class DerivedVariantB ; // Forward Declaration
class Variant
{
public:
// ...
Variant( const DerivedVariantB &obj )
{
std::cout << "\n DerivedVariantB \n";
}
};
Now, it's possible to call the constructor that takes the reference of type DerivedVariantB
instead of the template generated one.
Upvotes: 0