cytrinox
cytrinox

Reputation: 1946

Change constructor precedence

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

Answers (2)

James McNellis
James McNellis

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

Mahesh
Mahesh

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

Related Questions