Peaceful
Peaceful

Reputation: 480

How to combine template partial specialization and template argument deduction

My understanding is that template argument deduction is only for function templates, but function templates does not allow partial specialization. Is there a way to achieve both?

I basically want to achieve a function-like object (can be a functor) with the signature

template<class InputIterator1, class InputIterator2, class OutputIterator, int distribution>
void GetQuantity(InputIterator1 frist1, InputIterator1 last1, InputIterator2 first2, OutputIterator result, double supply, double limit);

Depending on the value of distribution, I want to have a couple of specializations of this template. And when I call this function,I basically do not want to specify all the type parameters, because they are many of them (and thus I need argument deduction)!

Upvotes: 1

Views: 768

Answers (2)

dyp
dyp

Reputation: 39101

An alternative to partially specializing a struct is to use std::integral_constant overloads:

template<class InputIterator1, class InputIterator2, class OutputIterator>
void GetQuantity(InputIterator1 first1, InputIterator1 last1,
                 InputIterator2 first2, OutputIterator result,
                 double supply, double limit,
                 std::integral_constant<int, 0>);

This can be called via a dispatcher:

template<int distribution,
         class InputIterator1, class InputIterator2, class OutputIterator>
void GetQuantity(InputIterator1 first1, InputIterator1 last1,
                 InputIterator2 first2, OutputIterator result,
                 double supply, double limit)
{
    GetQuantity(first1, last1, first2, result, supply, limit,
                std::integral_constant<int,distribution>{});
}

A default implementation can be provided via a conversion sequence:

template<int dist>
struct der_int_const : std::integral_constant<int, dist>
{}

template<int distribution,
         class InputIterator1, class InputIterator2, class OutputIterator>
void GetQuantity(InputIterator1 first1, InputIterator1 last1,
                 InputIterator2 first2, OutputIterator result,
                 double supply, double limit, der_int_const<distribution>{});

There's another way to even partially specialize even on the distribution parameter (simplified):

#include <iostream>
#include <type_traits>

struct X0{};
struct X1{};

template<int distribution, class It,
         class U = typename std::enable_if<distribution==0>::type>
void GetQuantity(It, X0={});

template<int distribution, class It,
         class U = typename std::enable_if<(distribution>1)>::type>
void GetQuantity(It, X1={});

C++03 version:

#include <boost/type_traits.hpp>

and then use boost::integral_constant instead of std::integral_constant

#include <boost/utility/enable_if.hpp>

struct X0{};
struct X1{};

template<int distribution, class It>
typename boost::enable_if_c<distribution==0>::type
GetQuantity(It, X0=X0()){ std::cout<<"0\n"; }

template<int distribution, class It>
typename boost::enable_if_c<(distribution>1)>::type
GetQuantity(It, X1=X1()){ std::cout<<"1\n"; }

Upvotes: 2

jrok
jrok

Reputation: 55395

You can achieve this with a functor thats templated on distribution and let the deduction be done on a call to operator():

template<int distribution>
struct GetQuantity {
    template<class InputIterator1, class InputIterator2, class OutputIterator>
    void operator()(
        InputIterator1 frist1, InputIterator1 last1, InputIterator2 first2, 
        OutputIterator result, double supply, double limit );
};

template<>
struct GetQuantity<0> {
    // specialized stuff
};

Upvotes: 3

Related Questions