views:

83

answers:

4

In templates as shown below, I would like the call Run(&Base::foo) succeed without the need to name the Base type twice (as is done in the compiling Run<Base>(&Base::foo) call). Can I have that? Possibly without adding a ton of Boost headers?

With the provided code, I get an error of:

prog.cpp:26: error: no matching function for call to ‘Run(bool (Base::*)())’

(you can fiddle with the snippet at http://ideone.com/8NZkq):

#include <iostream>

class Base {
public:
  bool foo() { return true; }
};

Base* x;

template<typename T>
struct Traits {
  typedef bool (T::*BoolMethodPtr)();
};

template<typename T>
void Run(typename Traits<T>::BoolMethodPtr check) {
  T* y = dynamic_cast<T*>(x);
  std::cout << (y->*check)();
}

int main() {
  Base y;
  x = &y;
  Run<Base>(&Base::foo);
  Run(&Base::foo); // why error?
}
+2  A: 

I think this is a non deduced context.

$14.8.2.5/5- "The non-deduced contexts are: — The nested-name-specifier of a type that was specified using a qualified-id."

I think this is the quote that applies in this case. But some template gods need to ratify my understanding.

Chubsdad
Have you memorized the Standard or what? `:)`
Prasoon Saurav
+2  A: 

When the compiler tries to match a template argument, it only considers the primary class type. In other words, when it encounters the expression:

Run(&Base::foo);

...and it's trying to figure out the template parameter for Run, it only considers the type of foo itself, and doesn't consider whatever class foo is a part of.

EDIT:

And the type of foo is bool(Base::*)(void), but what you want the compiler to find is just Base

John Dibling
The compiler can actuallu deduct Class from bool(Class::*)() in this context, see Bart's reply.
Alexander Rautenberg
+4  A: 

To pick apart a type, any type, use partial specialization. There is no function template partial specialization, so you'll need to directly parameterize the function on its argument type and retrieve the class type inside.

template< typename T >
struct get_host_class; // most types are not ptmfs: don't implement this

template< typename C >
struct get_host_class< bool (C::*)() > { // implement partial specialization
     typedef C host;
     typedef void sfinae; // disallow function for non ptmf arguments
};

template< typename T >
typename get_host_class<T>::sfinae Run( T check) {
    typedef T BoolMethodPtr; // or something
    typedef typename get_host_class< T >::host host;
}
Potatoswatter
Um... not a template expert here, so could you please elaborate on what 'sfinae' and 'ptmf' expand to? for me at least, that would make it easier to read :)
akavel
@aka: sfinae = Substitution Failure Is Not An Error, a trick for (among other things) emulating partial specialization in functions. ptmf = Pointer To Member Function, the kind of type that is causing you, and many others, grief.
Potatoswatter
akavel
+7  A: 

The T in Traits<T>::BoolMethodPtr is in a non-deduced context, so the compiler will not deduce automatically from the call what type T should be. This is because there could be code like this:

template<typename T>
struct Traits {
  typedef bool (T::*BoolMethodPtr)();
};

template<>
struct Traits<int> {
  typedef bool (Base::*BoolMethodPtr)();
};

Run(&Base::foo); /* What should T be deduced to? Base and int are both equally possible */

If you can do without the Traits<T> class, you can write Run as:

template<class Class>
void Run(bool (Class::*check)()) {
  Class* y = dynamic_cast<Class*>(x);
  std::cout << (y->*check)();
}

In this context, Class can be deduced to mean Base

Bart van Ingen Schenau
d'oh! Your answer is better than mine.
Potatoswatter
Sure I can do without! Aargh, so you're saying it's doable, and I've overcomplicated; LOL, life's funny :) Thanks a lot for the solution; regarding the explanation, it looks like I'll have to approach it some other day, after a good night's sleep ;) kudos to everybody on that thread, you'll get your +1s, all answers are very interesting, each one brings a part of the puzzle out
akavel