Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to work around partial specialization of function template?

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.

like image 818
user1899020 Avatar asked Mar 07 '14 15:03

user1899020


People also ask

What is the difference between partial specialization and template specialization?

Partial template specialization is a particular form of class template specialization. Usually used in reference to the C++ programming language, it allows the programmer to specialize only some arguments of a class template, as opposed to explicit full specialization, where all the template arguments are provided.

Is it possible to overload the function templates?

You may overload a function template either by a non-template function or by another function template. The function call f(1, 2) could match the argument types of both the template function and the non-template function.

What does template <> mean in C++?

A template is a C++ programming feature that permits function and class operations with generic types, which allows functionality with different data types without rewriting entire code blocks for each type.


4 Answers

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

like image 93
Arne Mertz Avatar answered Nov 15 '22 22:11

Arne Mertz


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.

like image 20
Angew is no longer proud of SO Avatar answered Nov 15 '22 21:11

Angew is no longer proud of SO


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...

like image 31
Nim Avatar answered Nov 15 '22 22:11

Nim


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>)
{
}
like image 33
Constructor Avatar answered Nov 15 '22 21:11

Constructor