1

I'm trying to create a template function that accepts pointer to a member functions (non-virtual). I'm working with MSVC2010

The following code works when the problematic line is commented out. The error reported by compiler is pretty explanatory, but I wonder somehow still surprising. How would you recommend working around this problem.

Thanks!

class Foo{ 
public:
    virtual void doFoo() {
        std::cout << "In foo" << std::endl;
    }
};

class Bar : public Foo{ 
public:
    void doBar() {
        std::cout << "In bar" << std::endl;
    }

};

template<class A>
void caller(A &a, void (A::*func)()) {
    (a.*func)();
}

int _tmain(int argc, _TCHAR* argv[])
{
    Bar bar;
    bar.doFoo();
    caller(bar, &Bar::doBar);
    caller(bar, &Bar::doFoo); // this line causes a compiler error
}

This fails on the following error.

error C2782: 'void caller(A &,void (__thiscall A::* )(void))' : template parameter 'A' is ambiguous
1>          c:\test\test\test.cpp(23) : see declaration of 'caller'
1>          could be 'Foo'
1>          or       'Bar'

I can work around the error by changing caller to

template<class A, class B>
void caller(A &a, void (B::*func)()) {
    (a.*func)();
}

But this introduces other subtle resolution bugs when considering overload resolutions. Ideally I'd like to only consider functions that can actually be applied to A.

Thanks!

3
  • I recommend you look into std::function and std::bind. Commented Oct 17, 2012 at 11:30
  • @Joachim: seems a bit excessive, though, to resort to std::function and type erasure just because the callback in our algorithm is constrained to be member function. That said, in a lot of cases it would be a bad idea to make that constraint at all. Commented Oct 17, 2012 at 11:45
  • The "unwanted" version will fail to compile if you pass function that can't be applied to the instance. I will be hard error, because it will fail in the body, which is not SFINAE context. As long as you don't have another overload for those cases (and I can't see any reason for that), it shouldn't be a problem. Commented Oct 17, 2012 at 12:14

2 Answers 2

2

You could use SFINAE to restrict the applicability of the second template

template<class A, class B>
void caller(A &a, void (B::*func)(), typename std::enable_if<std::is_base_of<B, A>::value, void>::type* =0)

This way, the overload will get out of the way for parameters that would cause a compile error otherwise. For reference, see std::is_base_of.

Sign up to request clarification or add additional context in comments.

3 Comments

It should be noted that no other overload probably exists anyway, so the error shouldn't actually be a problem in practice. But this is indeed the right way to control the behaviour.
I tried your example and gcc complained about type*= interpreting it as assignment, it seems. A space type* = fixes this.
That is indeed what I came up with eventually. Thanks!
0

As far as I can see it you must use unwanted version.

template<class A, class B>
void caller(A &a, void (B::*func)()) {
    (a.*func)();
}

Because the function is not defined on that object and thus type resolution does not apply cleanly.

But I would generally not advise to use this type of callback schematic. Either use the good old C style callbacks; function with a void* data pointer or something like sigc++.

The monster advantage with sigc++ is that you have generic signals that can be bound to almost anything, a function, a method or a fuctor and all typesafe.

(Please excuse that this is not a direct answer to your question, but a hint on how to take it further.)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.