28

I need to define a get method in two different ways. One for simple types T. And once for std::vector.

template<typename T>
const T& Parameters::get(const std::string& key)
{
    Map::iterator i = params_.find(key);
    ...
    return boost::lexical_cast<T>(boost::get<std::string>(i->second));
    ...
}

How can I specialize this method for std::vector. As there the code should look something like this:

template<typename T>
const T& Parameters::get(const std::string& key)
{
    Map::iterator i = params_.find(key);
    std::vector<std::string> temp = boost::get<std::vector<std::string> >(i->second)
    std::vector<T> ret(temp.size());
    for(int i=0; i<temp.size(); i++){
         ret[i]=boost::lexical_cast<T>(temp[i]);
    }
    return ret;    
}

But I do not know how to specialize the function for this. Thanks a lot.

andand
  • 17,134
  • 11
  • 53
  • 79
tune2fs
  • 7,605
  • 5
  • 41
  • 57
  • possible duplicate: http://stackoverflow.com/questions/1318458/template-specialization-of-template-class – bbtrb Nov 29 '11 at 17:17

2 Answers2

34

Don't specialize function template.

Instead, use overload.

Write a function template get_impl to handle the general case, and overload (not specialize) this to handle the specific case, then call get_impl from get as:

template<typename T>
const T& Parameters::get(const std::string& key)
{
     //read the explanation at the bottom for the second argument!
     return get_impl(key, static_cast<T*>(0) );
}

And here goes the actual implementations.

//general case
template<typename T>
const T& Parameters::get_impl(const std::string& key, T*)
{
    Map::iterator i = params_.find(key);
    return boost::lexical_cast<T>(boost::get<std::string>(i->second));
}

//this is overload - not specialization
template<typename T>
const std::vector<T>& Parameters::get_impl(const std::string& key, std::vector<T> *)
{
      //vector specific code
}

The static_cast<T*>(0) in get is just a tricky way to disambiguate the call. The type of static_cast<T*>(0) is T*, and passing it as second argument to get_impl will help compiler to choose the correct version of get_impl. If T is not std::vector, the first version will be chosen, otherwise the second version will be chosen.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
3

Erm. call it something else? e.g.

template<typename T>
const T& Parameters::getVector(const std::string& key)
{
  Map::iterator i = params_.find(key);
  std::vector<std::string> temp = boost::get<std::vector<std::string> >(i->second)
  // T is already a vector
  T ret; ret.reserve(temp.size());
  for(int i=0; i<temp.size(); i++){
     ret.push_back(boost::lexical_cast<typename T::value_type>(temp[i]));
  }
  return ret;  
}

You'll have to call this as:

foo.getVector<std::vector<int> > ("some_key");

Nothing in your question precludes this.

Now, if you really do need to use get(), then you have to rely on partially specializing a structure, as function partial specialization is not supported by the language.

This is a lot more complicated, for example:

template <typename T>
struct getter
{
  const T& operator()(std::string const& key)
  {
    // default operations
  }
};

// Should double check this syntax 
template <typename T>
struct getter<std::vector<T, std::allocator<T> > >
{
  typedef std::vector<T, std::allocator<T> > VecT;
  const VecT& operator()(std::string const& key)
  {
    // operations for vector
  }
};

Then in you method becomes:

template<typename T>
const T& Parameters::get(const std::string& key)
{
  return getter<T>()(key); // pass the structures getter needs?
}
Nim
  • 33,299
  • 2
  • 62
  • 101
  • Do you have to call it in another name? Wouldn't overloading just do the trick? – the_drow Nov 29 '11 at 17:34
  • 1
    @the_drow You cannot overload based on return type. – Christian Rau Nov 29 '11 at 18:01
  • @ChristianRau: Sure you can with some template magic. `template T ft(); template<> int ft() { return 1; } template<> float ft() { return 0; } struct f { template operator T() { return ft(); } }; int main() { int il = f(); float fl = f(); cout << il, fl; }` litb wrote this btw. – the_drow Nov 30 '11 at 00:06
  • @Nim: Instead of making `getter` a class template, you could make `operator()` a function template **and** instead of defining one `operator()`, define two `operator()`, one for each case (general as well as specific). That way you would *not* need to specialize `getter`. There is also one plus point (syntactically): you could call this as : `getter()(key)` instead of `getter()(key)`. – Nawaz Nov 30 '11 at 08:46
  • .... But either way, it has a drawback : `getter` cannot access members of `Parameters` class. So I think, it is better to make the actual implementations member of the class (like I've done), rather than defining new struct. – Nawaz Nov 30 '11 at 08:52
  • @Nawaz, definitely need to pass any state to the `getter` so that it can return the values. – Nim Nov 30 '11 at 08:58
  • @Nim: That can be done. But it makes thing unnecessarily complicate, for example; if `getter` needs to access `private/protected` members, then you've to make `getter` friend of the class. – Nawaz Nov 30 '11 at 09:04