2

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?

Dmitry J
  • 867
  • 7
  • 20
  • http://en.cppreference.com/w/cpp/types/enable_if – John Zwinck Mar 27 '17 at 14:08
  • @John Zwinck, Could you please provide an example? There are no examples with virtual functions by your link. Also, I don't see how this helps to avoid code duplication of functions `foo()` and `bar()`. – Dmitry J Mar 27 '17 at 14:15
  • @DmitryJ Could you provide example with `enable_if` that doesn't satisfy you? Because such case is classic for an `enable_if` usage. You don't want to retype `enable_if` in `Derived`? – stryku Mar 27 '17 at 14:25
  • @stryku, as I understand SFINAE helps only in case of overloads, not for optional defining member functions. – Dmitry J Mar 27 '17 at 15:34

1 Answers1

3

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;
};
Nir Friedman
  • 17,108
  • 2
  • 44
  • 72
  • [Here is an example](http://coliru.stacked-crooked.com/a/efd1855aeb6a3ca0) of a similar approach, which includes not just the conditional pure-virtual member, but conditionally implements it on the derived class. Unfortunately, it requires two nasty hacks: making the derived-clone implementation a friend of the derived class (presumably required so the clone-impl can access private members), and dynamic-casting from the derived-clone-impl type to the derived type. – cdhowie Mar 27 '17 at 15:30
  • **@Nir Friedman**, unfortunately, the main problem is how to define derived classes, conditionally implementing `clone()` function without duplicating all their code. Do you know the solution? **@cdhowie**, thank you, I'll try to study your link. – Dmitry J Mar 27 '17 at 15:43
  • @DmitryJ Yes, you use more CRTP and do something similar, where you put a class in between `Base` and `Derived` in the hierarchy. I don't think your original question was clear though in mentioning the implementation aspect though, thought you were referring to code duplication in the base class. – Nir Friedman Mar 27 '17 at 15:53
  • @cdhowie You should not need virtual inheritance nor friendship. – Nir Friedman Mar 27 '17 at 15:55
  • @DmitryJ I added the implementation to my answer. – Nir Friedman Mar 27 '17 at 16:01
  • **@NirFriedman**, thank you very much, I will try it right now and tell the results. – Dmitry J Mar 27 '17 at 16:10
  • @NirFriedman, I confirm that it works. I had use some tweaks because I have multilevel hierarchy with different `clone` implementations, but the idea stay the same. Thank you! – Dmitry J Mar 27 '17 at 18:19