tags:

views:

177

answers:

3

I have a templated class

template <typename Data>
class C
{
.....
}

In most situations, I depend on the compiler to let me substitute types for Data. I call methods foo(), goo() on objects of type Data, so what I substitute needs to provide that.

I now need to substitute int and string for my Data type. I do not want to specialize because the class is already too big and would require specializing each method (with only small code change).

My options (please tell me if there are more)

1) I can provide wrapper classes around int and string which implement the methods foo(), goo() etc

2) provide a traits class traits that calls foo() or goo() on objects of classes that provide foo(),goo() (these are my present substitutable classes) and specialize these classes for int and string.

Questions

1) what are the relative merits of 1 vs 2?

2) My traits classes will have static methods. Can a traits class have non-static methods as well? I see most traits classes define constants in the STL.

3) Do I make the traits classes global or should I pass them in as a template parameter for class C?

+1  A: 

1) what are the relative merits of 1 vs 2?

Wrappers around built-ins keep C simple. Traits keep the built-ins built-ins. :)
You could also try to factor out the code that differs depending on Data into a base class template and specialize that.

2) My traits classes will have static methods. Can a traits class have non-static methods as well? I see most traits classes define constants in the STL.

I'm not even sure it's still called "traits" if it has state.
FWIW, I usually categorize as traits only those which factor out information about other types. When it comes to behavior, I'd rather call it a policy. I do see, however, that std::char_traits doesn't fit these definitions. :( Anyway, if it it has non-static members, then it has state, and I wouldn't call this traits anymore.

3) Do I make the traits classes global or should I pass them in as a template parameter for class C?

Passing it as a template parameter only makes sense of you want users of C provide their own traits for whatever they pass in as Data. If there will always ever be one traits template for int the class can just use that. If users might come up with their own traits for int, they need a way to pass it to C.

sbi
A: 

Half a dozen of each? I don't know of any a priori reason to use one vs. the other in a generic sense. None of what you are asking makes me think you should chose one over the other. The only one thing I can think of is that choice 1 is probably going to require less changes to the existing function(s). On the other hand, clients won't be able to just use the function on primitives without creating the wrapper.

It's really a matter of specifics and you didn't provide them.

Noah Roberts
+3  A: 

You could specialize part of class like follows:

template <typename Data>
class C
{
  void foo();

  // lot of other stuff
};

// specialize part of class C 
// (some members of a class C will have specific 
//  implementation for specific types)
template<> void C<int>::foo() { std::cout << "int" << std::endl; }
template<> void C<std::string>::foo() { std::cout << "string" << std::endl; }
// same for goo

The syntax above allowed by C++ Standard 14.7/3 and 14.5.2/2. There's no need to rewrite all stuff from class C several times.

Note, that it is not allowed to partially specialize template class in such way. For instance, you cannot define different functions for Data and Data* types in this way.

Kirill V. Lyadvinsky
Thanks. I am bit confused by "Note, that you cannot partially specialize class in such a way". If it is guaranteed by the C++ standard, why is it not possible? If I specialize, I have to specialize the whole class?
duli
There's a difference between "partially specialize" and "full specialize part of a class".
Kirill V. Lyadvinsky
Check [this](http://codepad.org/slvQFZbW) sample. Note that function `test` exists in both cases, no need to rewrite it.
Kirill V. Lyadvinsky