0

Suppose a few class templates specializations have some common public interface. Is it possible to declare it just once?

Example:

// Example for a generator
class G
{
public:
    double generate(int channel);
};

template <typename Generator>
class A
{
public:
    double step();
protected:
    Generator g;
};

template <typename Generator, bool fixedMono>
class B;

template <typename Generator>
class B<Generator,true> : public A<Generator>
{
};

template <typename Generator>
class B<Generator,false> : public A<Generator>
{
public:
    void setNumChannels(int numChannels);
private:
    int numChannels;
};

template<typename Generator>
double B<Generator,true>::step() { return A<Generator>::g.generate(0); }

template<typename Generator>
double B<Generator,false>::step()
{
    double sum = 0;
    for (int i = 0; i < numChannels; ++i)
        sum += A<Generator>::g.generate(i);
    return sum;
}

This fails because the compiler doesn't recognize step being declared in the B specializations (which indeed, it isn't).

In a non-toy example the common interface could be larger than just a single function, and repeating its declaration in all specializations would not be desirable.

Is there good way to specify the common interface just once?

Please note suggestions to refactor the above example so there is no need for the template specializations to have a common interface are irrelevant, unless the refactoring method is one which can always be used to eliminate such interfaces. The question is whether declaring such a common interface just once, in cases where it is needed, is technically possible.

Danra
  • 9,546
  • 5
  • 59
  • 117

3 Answers3

0

One improvement would be to use CRTP. On its own it doesn't eliminate the need to declare a member function twice, once in each specialization, but at least the repeating declaration is of the private implementation member function, not of the public interface one, which is only declared once.

// Example for a generator
class G
{
public:
    double generate(int channel);
};

template <typename Derived, typename Generator>
class A
{
public:
    double step() { return static_cast<Derived&>(*this)->stepImpl(); };
protected:
    Generator g;
};

template <typename Generator, bool fixedMono>
class B;

template <typename Generator>
class B<Generator,true> : public A<B<Generator,true>, Generator>
{
private:
    double stepImpl();
    using A<B<Generator,true>, Generator>::g;
};

template <typename Generator>
class B<Generator,false> : public A<B<Generator,false>, Generator>
{
public:
    void setNumChannels(int numChannels);
private:
    double stepImpl();
    int numChannels;
    using A<B<Generator,false>, Generator>::g;
};

template<typename Generator>
double B<Generator,true>::stepImpl() { return g.generate(0); }

template<typename Generator>
double B<Generator,false>::stepImpl()
{
    double sum = 0;
    for (int i = 0; i < numChannels; ++i)
        sum += g.generate(i);
    return sum;
}
Danra
  • 9,546
  • 5
  • 59
  • 117
0

If you want to define a method in a class, you have first to declare it in the same class. Inheritance allow to share behaviour, but here, you reimplement the whole method anyway.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

The main problem is that you cannot achieve this because it is not allowed. A good explianation: https://stackoverflow.com/a/41578586/2504757

Below is another way. Using the Curiously recurring template pattern

Define an empty class for a default template parameter

//==================================================================
class NoNumChannels
{};

Take out the stepping into a class of it's own.

//==================================================================
template< typename Generator, typename NumChannels = NoNumChannels >
class Stepper;

template< typename Generator >
class Stepper< Generator, NoNumChannels > // Specialization case when No Numchannels. Just use the generator.
{
public:
    double step()
    {
        return g.generate( 0 );
    }

protected:
    Generator g;
};

template< typename Generator, typename NumChannels > // Otherwise use a class which has NumChannels
class Stepper
{
public:
    double step()
    {
        double sum = 0;
        for (int i = 0; i < (static_cast<NumChannels&>(*this).numChannels_v()); ++i) //Use Curiously recurring template pattern to access num of channels
            sum += g.generate( i );
        return sum;
    }

protected:
    Generator g;
};

Definition of B is slightly modified. For case where it doesn't have NumChannels just inherit from Stepper with Generator. Otherwise inherit from Stepper with Generator and self.

//==================================================================
template< typename Generator, bool fixedMono >
class B;

template< typename Generator >
class B< Generator, true > : public Stepper< Generator >
{};

template< typename Generator >
class B< Generator, false > : public Stepper< Generator, B< Generator, false > >
{
public:
    void setNumChannels( int numChannels );
    int numChannels_v();
private:
    int numChannels;
};
Robert Andrzejuk
  • 5,076
  • 2
  • 22
  • 31