Search code examples
c++c++11templates

Is there a way to call a template constructor from a specialized constructor?


Lets say I have this class:

template <class T>
class Test
{
    Test(T* x);

    const T* const t;
    int i{0};
};

I want t to always be initialized with x:

template <class T> Test<T>::Test(T* x) : t{x} {}

And I have two specializations:

template <> Test<Foo>::Test(Foo* x) : t{x} { i = 1; }
template <> Test<Bar>::Test(Bar* x) : t{x} { i = 2; }

Next, I'm extending the class with some other stuff, and that first (templated) constructor does a lot more than just setting t. I want to do all those things for both T = Foo and T = Bar.

Is there some way that I can call the templated constructor from the specialized constructors?

// This does not work, since it will create a delegation cycle
template <> Test<Foo>::Test(Foo* x) : Test(x) { i = 1; }
template <> Test<Bar>::Test(Bar* x) : Test(x) { i = 2; }

Solution

  • You can use a delegating constructor for this.

    You can create a private constructor, that takes the pointer for t, and an int for i. Then you can use that to set x and i, and run all of the shared code.

    That would look like:

    template <class T>
    class Test
    {
    public:
        Test(T* x) : Test(x, 0) { /*code for default case, runs after delegate*/ }
    private:
        Test(T* t, int i) : t(t), i(i) { /*code to run for everything*/ }
        const T* const t;
        int i;
    };
    
    template <> Test<Foo>::Test(Foo* x) : Test(x, 1) { /*code only for Foo, runs after delegate*/ }
    template <> Test<Bar>::Test(Bar* x) : Test(x, 2) { /*code only for Bar, runs after delegate*/ }
    

    Can the delegate constructor be the generic/templated constructor (with the same signature as the specific, specialized constructors for Foo and Bar)?

    No, that isn't possible. When you specialize a function template, you aren't creating a new function, but instead specifying that if T gets deduced to the type you specify in the specialization, then use the specialization definition in place of the generic one.

    That is why I have "all three constructors" (the generic and the two specializations) call Test(T* t, int i), which handles the code that is shared by all cases.