4

I have the following class;

template<int N, int M, int K>
class BaumWelch
{
  //lots of stuff
  const TransitionMatrixTemplate<N, M> randomA()
  { //.... }
}

now I would like to specialize the method randomA for N=1. How can I do it?

I tried following this question: Template specialization of a single method from a templated class, but it doesn't seem to work with partial specialization. This question: C++ partial method specialization seems more relevant, but it suggest specializing the whole class (which is quite big in my case). Is it possible to specialize the whole class, but actually specialize only this one method?

Community
  • 1
  • 1
Grzenio
  • 35,875
  • 47
  • 158
  • 240

3 Answers3

6

I would like to specialize the method randomA for N=1. How can I do it?

You've discovered that partial specialization for functions is not allowed.

However, you can fully specialize a "detail" implementation of the code.

  template<int TheN>
  detail_randomA();

  const TransitionMatrixTemplate<N, M> randomA()
  {
      return detail_randomA<N>();
  }

And outside the class declaration:

  template<int N, int M, int K>
  template<int TheN>
  BaumWelch<N,M,K>::detail_randomA()
  {
      //lots of stuff when N != 1
  }

  template<int N, int M, int K>
  template<>
  BaumWelch<N,M,K>::detail_randomA<1>()
  {
      //lots of stuff when N == 1
  }
Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
  • Alternately you could dispatch to another `detail` implementation and specialize that. – Mark B Apr 23 '13 at 16:12
  • `int buffer[N-1] = {0};` won't compile under your solution. – Yakk - Adam Nevraumont Apr 23 '13 at 16:12
  • @Yakk Correct (or it *shouldn't*). Was that code proposed for this function in the question? I don't see it now, but maybe there was an edit? – Drew Dormann Apr 23 '13 at 16:16
  • @DrewDormann nope, no implementation was provided. But the `if` solution does *not* mean you don't need specialization (which is something you claim in your answer). You might not need specialization, or you might need it. – Yakk - Adam Nevraumont Apr 23 '13 at 17:33
  • @Yakk Perhaps an edit to the effect of "Nothing shown requires specialization at all..."? – Drew Dormann Apr 23 '13 at 17:38
  • 1
    Doesn't work :(. In the else branch I divide by (N-1) and the gcc complaints that there is a division by zero. Also I think it will actually complain (with high warning level at least) that the if statement is always true/false (depending on the current instantiation). – Grzenio Apr 23 '13 at 17:44
  • @Grzenio Then the *detail* approach is relevant. See my edit. – Drew Dormann Apr 23 '13 at 17:53
2

You can't partially specialize function templates, but you can pass the work off to class templates. Here's a fully working example:

#include<iostream>
using namespace std;

// This first template isn't important, it's just the return value from your function
template <int N, int M>
struct TransitionMatrixTemplate {
        void print_me() const {
                cout << N << ',' << M << endl;
        }
};

// We need to announce the existence of the BaumWelch class template early here,
// in order that it can appear in the signature of our impl_randomA class.
template<int N, int M, int K>
struct BaumWelch;

// Now, the first important bit of code.  The default implementation
template<int N, int M, int K>
struct impl_randomA {
        static TransitionMatrixTemplate<N,M> f(BaumWelch<N,M,K> * This) {
                return TransitionMatrixTemplate<N,M>();
        }
};

// Next, is the partially specialized version.
template<int M, int K>
struct impl_randomA<1,M,K> {
        static TransitionMatrixTemplate<1,M> f(BaumWelch<1,M,K> * This) {
                cout << "<Special for N=1> ";
                return TransitionMatrixTemplate<1,M>();
        }
};

// Finally, The BaumWelch class and its call out to impl_randomA.
template<int N, int M, int K>
struct BaumWelch {
        const TransitionMatrixTemplate<N, M> randomA() {
                return impl_randomA<N,M,K> :: f(this);
        }
};

int main() {
        BaumWelch<2,3,4>() . randomA() . print_me();
        BaumWelch<1,3,4>() . randomA() . print_me();
}
Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
  • This looks rather promising. How should I split it between the `.h` and `.cpp` files? – Grzenio Apr 25 '13 at 09:43
  • @Grzenio, that's a matter of taste, to some extent. Some people would put everything involving templates into the header file. In this case, that's everything except the `main` function. – Aaron McDaid Apr 25 '13 at 13:33
1

The short answer is "you don't".

A way to make it easy to do what you want to do (have randomA act differently for N=1) would be to stuff const TransitionMatrixTemplate<N, M> randomA() into a CRTP parent, then partially specialize that for N=1.

template<typename D, int N, int M, int K>
struct Bob_Base {
  static_assert( std::is_base_of< D, Bob_Base<D, N, M, K> >::value, "D must be derived from Bob_Base" );
  D* self() { return static_cast<D*>(this);
  D const* self() const { return static_cast<D*>(this);
  const TransitionMatrixTemplate<N, M> randomA()
  {
    // use self()-> instead of this-> in here to access Bob methods and date
  }
};
template<typename D,int M, int K>
struct Bob_Base< D, 1, M, K > {
  static_assert( std::is_base_of< D, Bob_Base<D, N, M, K> >::value, "D must be derived from Bob_Base" );
  D* self() { return static_cast<D*>(this);
  D const* self() const { return static_cast<D*>(this);
  const TransitionMatrixTemplate<1, M> randomA()
  { /* N=1 version  */ }
};

template<int N, int M, int K>
struct Bob : Bob_Base<Bob<N, M, K>, N, M, K>
{
  //lots of stuff, but no TransitionMatrixTemplate
}

this is only important if the code where N==1 cannot compile (or vice versa).

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524