C++ Function Template Partial Specialization

C++ function template partial specialization?

Function partial specialization is not yet allowed as per the standard. In the example, you are actually overloading & not specializing the max<T1,T2> function.

Its syntax should have looked somewhat like below, had it been allowed:

// Partial specialization is not allowed by the spec, though!
template <typename T>
inline T const& max<T,T> (T const& a, T const& b)
{ ^^^^^ <--- [supposed] specializing here
return 10;
}

In the case of a function templates, only full specialization is allowed by the C++ standard, -- excluding the compiler extensions!

Parameterization and function template partial specialization is not allowed

Another way is to turn the templated constant into a constant argument which the compiler can optimise away.

step 1: define the concept of a rotate_distance:

template<unsigned int R> using rotate_distance = std::integral_constant<unsigned int, R>;

step 2: define the rotate functions in terms of overloads of a function which takes an argument of this type:

template<unsigned int R>
uint32_t LeftRotate(uint32_t v, rotate_distance<R>)

Now, if we wish we can simply call LeftRotate(x, rotate_distance<y>()), which seems to express intent nicely,

or we can now redefine the 2-argument template form in terms of this form:

template<unsigned int Dist, class T>
T LeftRotate(T t)
{
return LeftRotate(t, rotate_distance<Dist>());
}

Full Demo:

#include <iostream>
#include <stdint.h>
#include <utility>

template<unsigned int R> using rotate_distance = std::integral_constant<unsigned int, R>;

template<typename T, unsigned int R>
inline T LeftRotate(unsigned int v, rotate_distance<R>)
{
static const unsigned int THIS_SIZE = sizeof(T)*8;
static const unsigned int MASK = THIS_SIZE-1;
return T((v<<R)|(v>>(-R&MASK)));
}

template<unsigned int R>
uint32_t LeftRotate(uint32_t v, rotate_distance<R>)
{
__asm__ ("roll %1, %0" : "+mq" (v) : "I" ((unsigned char)R));
return v;
}

#if __x86_64__
template<unsigned int R>
uint64_t LeftRotate(uint64_t v, rotate_distance<R>)
{
__asm__ ("rolq %1, %0" : "+mq" (v) : "J" ((unsigned char)R));
return v;
}
#endif


template<unsigned int Dist, class T>
T LeftRotate(T t)
{
return LeftRotate(t, rotate_distance<Dist>());
}

int main(int argc, char* argv[])
{
std::cout << "Rotated: " << LeftRotate((uint32_t)argc, rotate_distance<2>()) << std::endl;
std::cout << "Rotated: " << LeftRotate((uint64_t)argc, rotate_distance<2>()) << std::endl;
std::cout << "Rotated: " << LeftRotate<2>((uint64_t)argc) << std::endl;
return 0;
}

pre-c++11 compilers

Prior to c++11 we didn't have std::integral_constant, so we have to make our own version.

For our purposes, this is sufficient:

template<unsigned int R> struct rotate_distance {};

full proof - note the effect of optimisations:

https://godbolt.org/g/p4tsQ5

Function template overloading - partial specialization

The first class above compiles and returns back the appropriate Foo

Yes, because

template<>
foo1& Foo::getFoo( Param a, Param b, Param c ) {
return getFoo1( a, b, c );
}

it's a full specialization of the template method getFoo()

template<class FooType>
static FooType& getFoo( Param a, Param b, Param c );

with the FooType type fixed to foo1.

And you can make a full specialization of a template function (or method).

However, in the second class Bar I keep getting compiler errors that function definition is not matching an existing declaration.

Sure.

Because you're trying to partial specialize the template method getBar()

template<typename Type, template<typename> class BarType, class... FuncParams>
static BarType<Type>& getBar( FuncParams... params );

fixing BarType to bar1

template<typename Type, class... FuncParams>
bar1<Type>& Bar::getBar( FuncParams... params ) {
return {};//getBar1( params... );
}

But you can't partial specialize a template function/method. It's forbidden by the language.

If you want something similar, you have to pass through the partial specialization of a struct (or class).

--- EDIT ---

The OP ask

You said, "you have to pass through the partial specialization of a struct( or class)." Okay; so there is a work around: would you be able to provide a small basic example?

There are many ways to use partial specialization of structs (classes) to bypass the non-partial-specialization limits for functions/methods.

In the following basic example I propose a template foo struct with a template func() method. The single template parameter for foo is the type returned by func(); the variadic template type list for func() is the list of types of arguments.

But you can play this game in differents modes.

#include <iostream>

template <typename>
struct bar1
{ template <typename ... Args> bar1 (Args && ...) { } };

template <typename>
struct bar2
{ template <typename ... Args> bar2 (Args && ...) { } };

template <typename>
struct foo;

template <typename T>
struct foo<bar1<T>>
{
template <typename ... Args>
static bar1<T> func (Args && ... as)
{ return { std::forward<Args>(as)... }; }
};

template <typename T>
struct foo<bar2<T>>
{
template <typename ... Args>
static bar2<T> func (Args && ... as)
{ return { std::forward<Args>(as)... }; }
};

int main()
{
foo<bar1<int>>::func(1, "two", 3.0);
foo<bar2<long>>::func(4.0f, "five", 6L);
}

Why function template cannot be partially specialized?

AFAIK that's changed in C++0x.

I guess it was just an oversight (considering that you can always get the partial specialization effect with more verbose code, by placing the function as a static member of a class).

You might look up the relevant DR (Defect Report), if there is one.

EDIT: checking this, I find that others have also believed that, but no-one is able to find any such support in the draft standard. This SO thread seems to indicate that partial specialization of function templates is not supported in C++0x.

EDIT 2: just an example of what I meant by "placing the function as a static member of a class":

#include <iostream>
using namespace std;

// template<typename T, typename U> void f() {} //allowed!
// template<> void f<int, char>() {} //allowed!
// template<typename T> void f<char, T>() {} //not allowed!
// template<typename T> void f<T, int>() {} //not allowed!

void say( char const s[] ) { std::cout << s << std::endl; }

namespace detail {
template< class T, class U >
struct F {
static void impl() { say( "1. primary template" ); }
};

template<>
struct F<int, char> {
static void impl() { say( "2. <int, char> explicit specialization" ); }
};

template< class T >
struct F< char, T > {
static void impl() { say( "3. <char, T> partial specialization" ); }
};

template< class T >
struct F< T, int > {
static void impl() { say( "4. <T, int> partial specialization" ); }
};
} // namespace detail

template< class T, class U >
void f() { detail::F<T, U>::impl(); }

int main() {
f<char const*, double>(); // 1
f<int, char>(); // 2
f<char, double>(); // 3
f<double, int>(); // 4
}

A workaround for partial specialization of function template?

Anytime you ask yourself "how to simulate partial specialization for functions", you can think "overload, and let partial ordering decide what overload is more specialized".

template<int N>
using int_ = std::integral_constant<int, N>;

class Meta
{
template<int N, typename T> static constexpr T ipow(T x)
{
return ipow<N, T>(x, int_<(N < 0) ? -1 : N>());
}

template<int N, typename T> static constexpr T ipow(T x, int_<-1>)
{
// (-N) ??
return static_cast<T>(1) / ipow<-N>(x, int_<-N>());
}

template<int N, typename T> static constexpr T ipow(T x, int_<N>)
{
return x * ipow<N-1>(x, int_<N-1>());
}

template<int N, typename T> static constexpr T ipow(T x, int_<0>)
{
return 1;
}
};

I think you wanted to pass -N instead of N at the comment-marked position.

c++ class template partial specialization without specializing all member functions

In my experience use inheritance method (2) when you actually want a new class that inherits from the template. Use method (1) when the partial specialization is sufficiently different from the general case or general case is not defined at all and you just declare a bunch of partial specializations to make use of SFINAE.

If you simply want to enable/disable methods of a class based on template parameters then utilize SFINAE. As this way you can centralize all your definitions in one place and reason about them more easily. This is the preferred method unless you want user to make the template specializations on their own - which isn't desirable in general.

Example of SFINAE:

template <class T, size_t N>
class X {
public:
void a() {...};
X<T, N> b(X<T, N> x);
// etc
static X<T, n> c() {...};

template<size_t uN = N, std::enable_if_t<uN==3,int>=0>
void d() {...};
protected:
T data[N];
};

Here, X<int,3> x; x.d(); will compile but X<int,2> x; x.d(); will not as d() method is disabled unless N==3.

You can read a lot more about usages of SFINAE in template functions and classes in numerous online guides (note that the syntax is quite different for functions and classes).

Partial Specialization of a variadic template function

You can do something like this:

template <class C, typename ...Arguments>
auto addStyleClassRecursiveImpl(int, C *c, Arguments... arg)
-> decltype(c->addStyleClass(arg...), void()) {
c->addStyleClass(arg...);
// ... C has addStyleClass
}

template <class C, typename ...Arguments>
void addStyleClassRecursiveImpl(char, C *c, Arguments... arg) {
// ... C has not addStyleClass
}

template <typename... T>
void addStyleClassRecursive(T ...&&t) {
addStyleClassRecursiveImpl(0, std::forward<T>(t)...);
// ...
}

The idea is to tag-dispatch the request to an internal implementation (called addStyleClassRecursiveImpl in the example) and use sfinae and function overloading to pick the right version up.


And of course, you cannot partially specialize a function template.

Function template partial specialization - are there any workaround?

Your syntax is all wrong. Make it

template <typename T, NodeCachingOptions opt>
T* CreateSObject(const MPath& ob)
{
CreateSObject_Impl<T, opt> temp;
return temp.call(ob);
}

You pass the value of type NodeCachingOptions as the second template paramter of CreateSObject_Impl, not the type itself.

You may want to make call a static member of CreateSObject_Impl, and write return CreateSObject_Impl<T, opt>::call(ob);



Related Topics



Leave a reply



Submit