2

I have an indexer function, and was attempting to specialize it for the standard container types, but get errors. I'm sure it's either simple or impossible, but I can't recall which. I would prefer these as a function-object, but I wasn't able to make that work either. Is it possible to specialize a template function for a template class

namespace std { //declarations of predefined indexable types
    template <class T, class A> class vector;
    //others are here too, but all have the same issue 
}
//default indexer
template <class T>
double indexer(const T& b) { //this seems to work fine
    return b.getIndex();
}
// base types
template<> double indexer<char>(const char& b) { return double(b); }
//other primitives are here too, and work fine
// standard library
template<class T, class A> 
double indexer<std::vector<T,A>>(const std::vector<T,A>& b) 
{ return b.empty() ? 0 : indexer(*b.cbegin()); } //line with error

error message:

error C2768: 'indexer' : illegal use of explicit template arguments

I would like for this to be specialization vs overloading, because I have a function A that takes a function object/pointer template parameter, and overloaded function A that calls the first A with a defaulted indexer.

template<class T, class function>
double A(T a, function F) { return F(a);} //complicated
template<class T>
double A(T a) {return A(a, indexer<T>);} //default wrapper

It seems highly likely this is a duplicate question, but I cannot seem to find one.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • 3
    Specialization of the function template is not needed - use overloading instead. See [here](http://www.gotw.ca/publications/mill17.htm) why to prefer overloading over specialize the function template. – Simon Aug 23 '11 at 20:09
  • You cannot partially specialize function templates. You could wrap the function into a struct, or just create separate overloads as @Simon says. – Kerrek SB Aug 23 '11 at 20:17
  • Is there a way for the wrapper `A` to select a correct indexer type if it's not templated? – Mooing Duck Aug 23 '11 at 20:19

3 Answers3

2

You cannot partially specialize template functions, only template classes.

use overloading instead:

namespace std { //declarations of predefined indexable types
       template <class T, class A> class vector;
}
//default indexer
template <class T>
double indexer(const T& b) { return b.getIndex(); }

double indexer(const char& b) { return double(b); }

template<class T, class A>
double indexer(const std::vector<T,A>& b)
{ return b.empty() ? 0 : indexer(*b.cbegin()); }
Arvid
  • 10,915
  • 1
  • 32
  • 40
  • you can specialize template functions. You can't **partially** specialize them. If you specialize them you need explicit types. – Yochai Timmer Aug 23 '11 at 20:31
1

Since functions cannot be partially specialized, I can replace the indexer functions with function objects. This seems to work fine, and solves all my problems.

namespace std { //declarations of predefined indexable types
    template <class T, class A> class vector;
}
template <class T>
struct indexer { 
    double operator()(const T& b) const 
    { return b.getIndex(); } 
};
template<> struct indexer<char> { 
    double operator()(const char& b) const 
    { return double(b); } 
};
template<class T, class A> struct indexer<std::vector<T,A>> { 
    double operator()(const std::vector<T,A>& b) const 
    { return b.empty() ? 0 : indexer(*b.cbegin()); } 
};

template<class T, class function>
double A(T a, function F) { return F(a);} //complicated
template<class T>
double A(T a) {return A(a, indexer<T>());} //default wrapper
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
0

Here's a solution based solely on overloads, using C++11-style variadic templates:

template <typename T>
T print(const T & t)
{
  std::cout << t << std::endl;
  return t;
}

template <template <typename...> class Container, typename ...Args>
typename Container<Args...>::value_type print(const Container<Args...> & v)
{
  typedef typename Container<Args...>::value_type T;

  if (v.empty()) std::cout << "nil" << std::endl;
  else std::cout << *v.begin() << std::endl;

  return v.empty() ? T() : *v.begin();
}

If you want to be fancy and if you have access to an is_container type trait (e.g. taken from the pretty printer), you can make the container overload specific to containers, using enable_if:

template <template <typename...> class Container, typename ...Args>
typename std::enable_if<is_container<Container<Args...>>::value,
                        typename Container<Args...>::value_type>::type
print(const Container<Args...> & v)
{
  /* ... */
}
Community
  • 1
  • 1
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084