Museful
Museful

Reputation: 6959

Declare member function only for specific template instantiation of class

Is it possible to declare a member function only for specific template instantiations of a class? This is why I want to do it:

// Polynomial<N> is a polynomial of degree N
template<int N>
class Polynomial {
public:
    //... various shared methods e.g...
    double eval(double x) const;
    Polynomial<N-1> derivative() const;
    Polynomial<N+1> integralFrom(double x0) const;
    // ... various shared operators etc.

    double zero() const; // only want Polynomial<1> to support this
    // only want Polynomial<2> and Polynomial<1> to support the following
    //     because the solutions rapidly become too difficult to implement
    std::vector<double> zeros() const;
    std::vector<double> stationaryPoints() const { return derivative().zeros();}

private:
    std::array<double,2> coeffs;
}

My current workaround is to just throw an exception from Polynomial<N>::zeros() for N>2 but it would have been nice to detect the problem at compile-time.

Upvotes: 1

Views: 489

Answers (3)

Pumkko
Pumkko

Reputation: 1593

You can also use std::enable_if to SFINAE away the zero function.

template< int I >
class Poly {

public:
    template<int Ib = I, typename = std::enable_if_t<Ib == 1> > 
    double zero() 
    {
        return 42;
    }
};

int main()
{
    Poly< 10 > does_not_compile;
    does_not_compile.zero();

    //Poly< 1 >  compile;
    //compile.zero();
}

Upvotes: 1

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275350

You can use CRTP to implement zeros and zero in a base class that knows about the derived one.

Then conditionally derive from one that has zeros and/or zero, or not.

template<class P>
struct zeros { /* todo */ };

template<class P>
struct zero:zeros<P> { /* todo */ };

struct nozero {};

template<int N>
struxt Polynomial:
  std::conditional_t<
    (N==1),
    zero<Polynomial<N>>,
    std::conditional_t<
      (N==2),
      zeros<Polynomial<N>>,
      nozero
    >
  >
{
  // body
};

Upvotes: 0

EFenix
EFenix

Reputation: 831

Your solution is template specialization, in this case, full specialization.

However, I think you would need to think about your design. Generally, it is not a good idea to define different interfaces for different cases because you are not taking advantage of the abstraction.

For example, think about your stationaPoints() function. This functions should not be available for Polynomial<2> because the derivative is a Polynomial<1> which does not have the zeros() function. Your solution is to add the zeros function to the Polynomial<1> in order to homogenize the interface.

For your case, I guess you would like a solution which includes a N-1 c-vector inside the polynomial type with the zeros and a zero(i) function to get them. Something like this:

template <int N>
class Polynomial {
    double _coefs[N+1];
    double _zeros[N];
public:
    double zero(size_t i) const { assert(i<N); return _zeros[i]; }
    // ...
};

In this case, you could decide the strategy to calculate de zeros, depending on your application: constructor?, adding a boolean to know if it has been calculated then it is calculated in the first call to zero, etc...

I guess this solution could be more interesting for you because if you prefer a c-vector to store the coeficients, you wouldn't like the vector based interface of your zeros() function, would you?

Upvotes: 0

Related Questions