views:

326

answers:

5

I have the following problem:

class Base
{
};

class Derived : public Base
{
};

class Different
{
};

class X
{
public:
  template <typename T>
  static const char *func(T *data)
  {
    // Do something generic...
    return "Generic";
  }

  static const char *func(Base *data)
  {
    // Do something specific...
    return "Specific";
  }
};

If I now do

Derived derived;
Different different;
std::cout << "Derived: " << X::func(&derived) << std::endl;
std::cout << "Different: " << X::func(&different) << std::endl;

I get

Derived: Generic
Different: Generic

But what I want is that for all classes derived from Base the specific method is called. So the result should be:

Derived: Specific
Different: Generic

Is there any way I can redesign the X:func(...)s to reach this goal?

EDIT:

Assume that it is not known by the caller of X::func(...) if the class submitted as the parameter is derived from Base or not. So Casting to Base is not an option. In fact the idea behind the whole thing is that X::func(...) should 'detect' if the parameter is derived from Base or not and call different code. And for performance reasons the 'detection' should be made at compile time.

A: 

Just typecast derived to base

X::func((Base*)&derived)

it works....

harishvk27
In C++, it's preferable to use static_cast or dynamic_cast to C-style casts. (c.f. Stevens)
Shaggy Frog
Unfortunately this is not possible (see my edit)
rstevens
+1  A: 

The expression:

X::func(derived)

Means that the compiler will generate a declaration and code that effectively has this signature:

static const char *func(Derived *data);

which turns out to be a better match than your:

static const char *func(Base *data);

The template function will be used for anything that is legal for typename, e.g. any class you use as T and it will effectively exclude the Base version of your function from being used, due to compile time policy.

My suggestion is to use specialization in X for your specific types, i.e.:

template <typename T>
  static const char *func(T *data)
  {
    // Do something generic...
    return "Generic";
  }

template <>
  static const char *func(Derived *data) // 'Derived' is the specific type
  {
    // Do something specific...
    return "Specific";
  }

Hope that works!

jscharf
No, it doesn't work.
avakar
Nope, that doesn't work. Just tried it with gcc. Makes sense since you still get a better match with the generic version.
Troubadour
Whoops - meant to specialize as static const char *func(Derived *data)...those two should at least be equal in the compiler's eyes. Hopefully it picks the one explicitly specialized...
jscharf
@avakar: Right...it's the same result.
rstevens
Works here. Make sure your specialization is outside the class definition. Basically, move it to the parent namespace and replace 'static' with 'inline' and add 'X::' before the function name to get it to compile.
strager
@strager: Yes, he's updated the code to something that works now.
Troubadour
@jscharf: You haven't addressed the "...what I want is that for all classes derived from Base.." part of the question. Are you proposing a specialisation for each possible subclass of Base?
Troubadour
Troubadour is right, this solution is flawed.
avakar
@jscharf: Your update unfortunately does not fulfill my requirement in the edit of my question.
rstevens
Function template specialization is a very bad idea. The rules of resolving are very confusing. Read this: http://www.gotw.ca/publications/mill17.htm
Tadeusz Kopec
+6  A: 

You must use SFINAE for this. In the following code, the first function can be instantiated if and only if you pass something that can't be (implicitly) converted to Base *. The second function has this reversed.

You might want to read up on enable_if.

#include <iostream>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits.hpp>

class Base {};
class Derived : public Base {};
class Different {};

struct X
{
    template <typename T>
    static typename boost::disable_if<boost::is_convertible<T *, Base *>,
        const char *>::type func(T *data)
    {
     return "Generic";
    }

    template <typename T>
    static typename boost::enable_if<boost::is_convertible<T *, Base *>,
        const char *>::type func(T *data)
    {
     return "Specific";
    }
};

int main()
{
    Derived derived;
    Different different;
    std::cout << "Derived: " << X::func(&derived) << std::endl;
    std::cout << "Different: " << X::func(&different) << std::endl;
}
avakar
Any way without using boost?
rstevens
Well, `enable_if` is pretty easy to implement. However, `is_convertible` seems to be quite a different story. Personally, I wouldn't want to implement it on my own.
avakar
I took a look into the is_convertible.hpp....It's some kind of monster :-)
rstevens
Precisely. A lot of it however seems to be workarounds for various non-compliant compilers. Perhaps there is a short elegant version hidden in that file somewhere. I really don't feel like searching for it though, and I'm betting neither do you. I'd go with Boost :-)
avakar
@avakar: Not necessary (see my own answer)
rstevens
However, note there can be two notions: pointer convertibility, and universal convertibility. The following tests pointer convertibility: Basically, it is: `template<typename D, typename B> struct is_ptr_convertible { static char( static char( static bool const value = (sizeof is((D*)0) == 1); };`
Johannes Schaub - litb
A: 

If you are using boost, you can do it with some template metaprogramming:

#include <boost/type_traits/is_base_of.hpp>

class X
{
private:
    template <typename T>
    static const char *generic_func(T *data)
    {
        // Do something generic...
        return "Generic";
    }

    template <typename T>
    static const char *base_func(T *data)
    {
        // Do something specific...
        return "Specific";
    }

public:
    template <typename T>
    static const char* func(T* data)
    {
        if (boost::is_base_of<Base, T>::value)
            return base_func(data);

        return generic_func(data);
    }
};

The is_base_of metafunction is evaluated at compile time and the optimizer will most probably remove the dead branch of the if in the func function. This approach allows you to have more than one specific case.

Bojan Resnik
+6  A: 

I found a VERY easy solution!

class Base
{
};

class Derived : public Base
{
};

class Different
{
};

class X
{
private:
  template <typename T>
  static const char *intFunc(const void *, T *data)
  {
    // Do something generic...
    return "Generic";
  }

  template <typename T>
  static const char *intFunc(const Base *, T *data)
  {
    // Do something specific...
    return "Specific";
  }

public:
  template <typename T>
  static const char *func(T *data)
  {
    return intFunc(data, data);
  }
};

This works great and is very slim! The trick is to let the compiler select the correct method by the (otherwise useless) first parameter.

rstevens
+1, I should have thought of that. This trick is actually used a lot in STL to select between various versions of algorithms based on the category of passed iterators.
avakar
Keep the Boost version in mind, though, it will work with other traits than convertibility.
avakar
@avakar: I will do. And thanks for your time spent on the question even if I use my solution for the specific problem.
rstevens
Nice solution
jscharf
thanks for posting, it's indeed a nice solution.
ttvd