views:

109

answers:

6

I have the following struct:

template <typename T>
struct Odp
{
    T m_t;
};

I want to specialize it so I can add an operator so the type plays nicely with STL sets. (I can't modify Odp directly; it's legacy code.) Here are two methods I see of doing it:

struct Ftw : public Odp<int>
{
    bool operator==(const Ftw& rhs)
    {
        return m_t == rhs.m_t;
    } 
};

struct FtwContain
{
    Odp<int> odp;
    bool operator==(const FtwContain& rhs)
    {
        return odp.m_t == rhs.odp.m_t;
    }
};

Is there any reason to prefer the second over the first? The first method appears to allow cleaner code:

Ftw ftw;
ftw.m_t = 2;

FtwContain ftwContain;
ftwContain.odp.m_t = 2;

(Also, there's a chance that I'm confused about what the term "template specialization" means.)

A: 

Why not deriving Odp to MyOdp, put your (generic) code in it and just make Ftw derive from Odp (as in your first example) or using a typedef ? By the way that not specialization but instanciation. Template specialization is when you (re)define a method for a specific type.

mb14
A: 

I usually prefer composition over inheritance, but it really depends on the design. Is Ftw a type of Odp or does Ftw contain an Odp.

I wouldn't choose the method based on cleaner code (since it's not that much of a difference), I would choose the method based on conceptually what is the relationship between Odp and Ftw.

Starkey
+1  A: 

You may indeed be confused about the terminilogy. (Partial) template specialization normally referes to a specific implementation of a templated class /struct for a dedicated type. I.e. you may have a generic template class Hash that provides hash values for types using a method getHash. This method then has a generic implementation, that doesn't care about the type, and maybe a special implementation for hash values on strings:

// default implementation 
template<typename T> class Hash { int getHash(T val) { return val; } }
// string implementation
template<> class Hash<std::string> { int getHash(std::string val) { return val[0] || val[1]; } }

What you are doing in ur examples however is not template specialization but inheritance (in the first approach) and using the Odp template as a client. In both cases, if anyone uses the Odp template as in Odp<int> odp, the original implementation will be used, which may not be what you want. If you would use proper template specialization, Odp<int> would refer to your specialized code.

inflagranti
+7  A: 

I don't believe there is any need to create a new type - simply write a free function:

template <typename T>
bool operator==( const Odp<T> & a, const Odp <T> & b ) {
    return a.m_t == b.m_t;
}
anon
A: 

In the case you mentioned, I think a free function is possibly the cleanest way with the least amount of rebuild issues. Put this free function in a separate cpp file and you should be good to go.

Possible cases for derivation

  1. You would want to derive if you have to pass your object to some function which takes a base-class type
  2. Is the derived class a type of the first type. If so, yes (eg., a carnivore is an animal) 3.If there are protected methods in the base class that you want to use in your derived class. I am not sure if the structure you mentioned is the complete code or only the relevant section. If it is not, then this might be one reason you want to derive.

Possible cases for containing

  1. You merely want to use the class and there is no is-a relationship. TBH, one can simulate an is-a with containing objects too, where in the container type acts like a proxy for the contained-type (I think this is a design pattern, but am not sure of the name of the pattern).
  2. You are interested in using only one or two methods, and there is no worry of a shared state
  3. This object is never passed to any other interface which requires a base class (one can always pass the contained object, but that looks dirty. Also, toss in virtual functions and things are different. Sorry, I digress).
Gangadhar
A: 

As Neil mentions, operator== can well be a free function.

Another option: standard library allows the use of custom predicate objects. In this case:

#include <set>

template <typename T>
struct Odp
{
    T m_t;
};

struct CompareOdp
{
    template <class T>
    bool operator() (const Odp<T>& a, const Odp<T>& b) const
    {
        return a.m_t < b.m_t;
    }
};

int main()
{
    std::set<Odp<int>, CompareOdp > my_set;
    Odp<int> value = {10};
    my_set.find(value);
}

(Not sure, whether it might be a better idea to make the whole predicate a template. Making just operator() a template seems to make it easier to use, as it leaves more things to the compiler to figure out. Not sure if it could back-fire in some scenarios.)


Also note that std::set uses a predicate for ordering (by default std::less<X>), not for equality tests.

UncleBens