Dmitry J
Dmitry J

Reputation: 927

optional virtual function in template hierarchy depending on parameter

I have a template hierarchy, and I want it to have clone() function depending on whether template type is copy-constructible. As a first step I want to begin with additional parameter bool Clonable:

template<class T, bool Clonable>
class Base {
  T t;
  void foo();
  virtual void bar();
  virtual unique_ptr<Base> clone() const = 0; //should be only for Clonable=true
};

template<class T, bool Clonable>
class Derived : public Base<T, Clonable> {
  virtual void bar() override;
  virtual unique_ptr<Base> clone() const override; ////should be only for Clonable=true
};

Unfortunately, instantiation of virtual functions does not depend on whether they are called or not. So I suppose that I should go with partial specialization. However, straightforward way leads to very much of code duplication. Could anyone recommend the way to achieve this with minimum code duplication?

Upvotes: 2

Views: 1091

Answers (1)

Nir Friedman
Nir Friedman

Reputation: 17704

Unfortunately, contrary to a few of the comments here, SFINAE cannot help you here. That's because a non-template member of a template class is not considered a template, and therefore cannot be SFINAE'ed out: http://coliru.stacked-crooked.com/a/258e20a0293d93f0. The standard approach to solve this would typically to make it a template in a trivial way:

template <class U = T, std::enable_if ... >
virtual std::unique_ptr<Base> clone() const = 0;

But virtual functions can't be templates, so this doesn't work.

The way to avoid repetition in this case is to inherit from a class that conditionally has the member:

template <class Base, bool Cloneable>
struct CloneInterface;

template <class Base>
struct CloneInterface<Base, false> {};

template <class Base>
struct CloneInterface<Base, true> {
  virtual unique_ptr<Base> clone() const = 0;
}

And now you just inherit:

template<class T, bool Clonable>
class Base : CloneInterface<Base<T, Clonable>, Clonable> {
  T t;
  void foo();
  virtual void bar();
};

Notice that we inherit from a base class that is templated on the derived (the derived class in question is called Base, to make things more confusing :-) ). This technique is called CRTP and it is quite powerful as it can inject interface and implementations into classes, and as you can see can do so conditionally as well.

To get the implementation, we use CRTP again:

template <class T, bool Clonable, class D>    
struct BaseHelper;

template <class T, class D>    
struct BaseHelper<T, false, D> : Base<T, false> {};

template <class T, class D>    
struct BaseHelper<T, true, D> : Base<T, true> {
  unique_ptr<Base<T, true>> clone() override { return make_unique<D>(static_cast<D&>(*this)); }
};

template<class T, bool Clonable>
class Derived : public BaseHelper<T, Clonable, Derived<T, Clonable>> {
  virtual void bar() override;
};

Upvotes: 3

Related Questions