10

For example, I have a class:

class A
{
    enum {N = 5};
    double mVariable;

    template<class T, int i>
    void f(T& t)
    {
        g(mVariable); // call some function using mVariable.
        f<T, i+1>(t); // go to next loop
    }

    template<class T>
    void f<T, N>(T& t)
    {} // stop loop when hit N.
};

Partial specialization is not allowed in function template. How do I work around it in my case?

I slightly changed the example of Arne Mertz, like:

template<int n>
struct A
{
    enum {N = n};
    ...
};

and use A like:

A<5> a;

The I cannot compile on Visual Studio 2012. Is it a compiler bug or something else? It is quite strange.

EDIT: Checked. It is a Visual Studio bug. :(

I think Nim gives the most simple way to implement it.

Eitan Myron
  • 159
  • 1
  • 4
  • 16
user1899020
  • 13,167
  • 21
  • 79
  • 154

4 Answers4

7

The most straight forward solution is to use a template class instead of a function:

class A
{
    enum {N = 5};
    double mVariable;

    template <class T, int i>
    struct fImpl {
      static_assert(i<N, "i must be equal to or less than N!");
      static void call(T& t, A& a) {
        g(a.mVariable);
        fImpl<T, i+1>::call(t, a);
      }
    };

    template<class T>
    struct fImpl<T,N> {
      static void call(T&, A&)  {} // stop loop when hit N.
    };

 public:

    template<class T, int i>
    void f(T& t)
    {
        fImpl<T, i>::call(t,*this);
    }

};

Example link

Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
  • Should not the first call of `fImpl::call`, in `f`, be indexed by `i` ? – Matthieu M. Mar 07 '14 at 15:53
  • 2
    +1 but just a nitpick: if `A` is itself a class template, one can run into difficulties with specializing nested templates (depending on partial vs full specialization). So in that case it is better to move that stuff into a `detail` namespace. – TemplateRex Mar 07 '14 at 16:02
  • @TemplateRex yes, but in that case `fImpl` must either be made a friend of all instantiations of `A` or a handle to the needed internals of `A` (in this case, of `mVariable`) has to be passed around. Moving both `fImpl` and `mVariable` into a nontemplated base class should be considered as well. – Arne Mertz Mar 07 '14 at 16:24
  • This method is straightforward. I slightly modified your example as above. I got compilation error. Would you please take a look at it? Thanks. I checked it with other compilor. It is a Visual Studio bug. – user1899020 Mar 07 '14 at 16:25
4

You can define a helper class:

template <int i, int M>
struct inc_up_to
{
  static const int value = i + 1;
};

template <int i>
struct inc_up_to<i, i>
{
  static const int value = i;
};


template<class T, int i>
void f(T& t)
{
    if (i < N) {
        g(mVariable); // call some function using mVariable.
        f<T, inc_up_to<i, N>::value>(t);
    }
}

It stops the compile-time recursion by making f<T, N> refer to f<T, N>, but that call is avoided by the run-time condition, breaking the loop.

A simplified and more robust version of the helper (thanks @ArneMertz) is also possible:

template <int i, int M>
struct inc_up_to
{
  static const int value = (i >= M ? M : i + 1); // this caps at M
  // or this:
  static const int value = (i >= M ? i : i + 1); // this leaves i >= M unaffected
};

This doesn't even need the partial specialisation.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • 1
    you could simplify the thing *and* catch the `i>N` issue all our implementations have by defining only the common case with `value = (M>=N) ? i : i+1` and completely leaving out the specialization. – Arne Mertz Mar 07 '14 at 15:52
2

With c++11 support, you can do the following:

#include <iostream>
#include <type_traits>
using namespace std;

struct A
{
    enum {N = 5};
    double mVariable;

    void g(int i, double v)
    { std::cout << i << "  " << v << std::endl; }

    template<int i, class T>
    typename enable_if<i >= N>::type f(T& t)
    {} // stop loop when hit N.

    template<int i, class T>
    typename enable_if<i < N>::type f(T& t)
    {
        g(i, mVariable); // call some function using mVariable.
        f<i+1, T>(t); // go to next loop
    }

};

int main(void)
{
    A a;
    int v = 0;
    a.f<0>(v);
}

Main reason I like is that you don't need any of the cruft as required by the previous answers...

Constructor
  • 7,273
  • 2
  • 24
  • 66
Nim
  • 33,299
  • 2
  • 62
  • 101
  • 1
    Just wanted to add that techincally you don't need C++ 11 for this. enable_if is a class that can be implemented in any C++ version using SFINAE. – Yochai Timmer Mar 07 '14 at 17:40
  • Of course in real life the definition of `struct A` is probably in a header file, so you certainly would not type '`using namespace std;`' before the struct, and would just spell out `std::enable_if`. – aschepler Mar 07 '14 at 20:28
  • Caveat: Looks like while I've been away several folks have modified my original code. There was a *reason* I used the aliases, it makes the whiole meta programming side of things a lot easier... – Nim Mar 07 '14 at 21:42
  • @Nim it makes it easier to type but not so easy to read for people who don't know the stuff yet. – Arne Mertz Mar 10 '14 at 09:05
2

You can emulate partial specialization of function template with function overloading:

#include <type_traits>

class A
{
    enum {N = 5};
    double mVariable;

    // ...

    void g(double)
    {
        // ...
    }

public:

    template<class T, int i = 0>
    void f(T& t, std::integral_constant<int, i> = std::integral_constant<int, i>())
    {
        g(mVariable);
        f(t, std::integral_constant<int, i + 1>());
    }

    template<class T>
    void f(T& t, std::integral_constant<int, N>)
    {
    }

};

Example of using:

A a;
int t = 0;

a.f(t);
a.f(t, std::integral_constant<int, 2>()); // if you want to start loop from 2, not from 0

It is a C++11 solution, however (not so much because of std::integral_constant class, but because of default template parameter of function template). It can be made shorter using some additional C++11 features:

template<int i>
using integer = std::integral_constant<int, i>;

template<class T, int i = 0>
void f(T& t, integer<i> = {})
{
    g(mVariable);
    f(t, integer<i + 1>());
}

template<class T>
void f(T& t, integer<N>)
{
}
Constructor
  • 7,273
  • 2
  • 24
  • 66