6

I have a template base class which has a constructor for conversion from any other template instantiation of that class, like this:

template <class T>
class FooBase
{
public:

    FooBase()
    {
    }

    template <class U>
    FooBase(const FooBase<U>& other)
    {
        std::cout << "FooBase<U>" << std::endl;
    }
};

Notice how there is no copy constructor defined.

I then have a derived template class which does have a copy constructor, as well as the constructor used for conversion:

template <class T>
class Foo : public FooBase<T>
{
public:

    Foo()
    {
    }

    Foo(const Foo& other) :
        FooBase<T>(other)
    {
    }

    template <class U>
    Foo(const Foo<U>& other) :
        FooBase<T>(other)
    {
    }
};

Because FooBase doesn't have a copy constructor, this results in FooBase<T>(other) making a call to the compiler-generated copy constructor. Which means if I run this:

int main()
{
    Foo<int> a;
    Foo<int> b(a);

    return 0;
}

The output is nothing, when it should be FooBase<U>.

Of course, I could try to solve the issue by creating a copy constructor in FooBase and using delegating constructors:

    FooBase(const FooBase& other)
        : FooBase<T>(other)
    {
    }

But unfortunately that doesn't work, and it would result in a recursive call as the compiler helpfully points out:

warning C4717: 'FooBase<int>::FooBase<int>': recursive on all control paths, function will cause runtime stack overflow

So the only solution would be to duplicate the logic into both constructors.

Is there any way around this that doesn't involve code duplication or a separate initialization function?

2
  • That's because FooBase<T> actually names the current class, and thus the delegation goes back to the copy constructor, not to the constructor template. Commented Apr 1, 2017 at 22:52
  • You could use directly a template typename instead of FooBase<U>, however you would incur in this problem Commented Apr 2, 2017 at 1:34

1 Answer 1

4

You could have a third, private constructor that both the copy constructor and the constructor template delegate to and which contains the actual work:

template <typename T> class FooBase
{
    struct Tag{};

    template <typename U>  // May have U = T
    FooBase(Tag, const FooBase<U> & rhs)
    {
        // actual implementation
    }

public:
    FooBase(const FooBase & rhs) : FooBase(Tag(), rhs) {}

    template <typename U>  // Never selects U = T
    FooBase(const FooBase<U> & rhs) : FooBase(Tag(), rhs) {}
};
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.