The template parameter is ambiguous: the template argument could not be output

I am making some kind of shell that looks like this:

#include <iostream> template<class T, class Value> void Apply(void (T::*cb)(Value), T* obj, Value v) { (obj->*cb)(v); } class Foo { public: void MyFunc(const int& i) { std::cout << i << std::endl; } const int& GetValue() { return i_; } private: int i_ = 14; }; int main() { Foo f; Apply(&Foo::MyFunc, &f, f.GetValue()); } 

And I get this error:

  • Apply : no matching overloaded function was found.
  • void Apply(void (__thiscall T::* )(Value),T *,Value) : the template parameter Value ambiguous, can be int or const int & .
  • void Apply(void (__thiscall T::* )(Value),T *,Value) : could not infer the template argument for Value from const int .

So, I understand that it comes from subtracting the template parameter, however, I do not understand how to do this. Why has Value not been evaluated before const int& both times?

+8
c ++ templates ambiguous ambiguous-call template-deduction
source share
2 answers

Why does it not work

Currently, the Value template parameter is displayed in two different places when calling Apply : from the pointer to the argument of the member function and from the last argument. From &Foo::MyFunc , Value is inferred as int const& . From f.GetValue() , Value is inferred as int . This is due to the fact that the reference and upper levels of cv-qualifiers are discarded to subtract the template. Since the two outputs are different for the Value parameter, no output is possible - which removes Apply() from the overload set, and as a result we do not have a viable overload.

How to fix it

The problem is that Value is displayed in two different places, so let's just prevent this. One way is to wrap one of the uses in an undetectable context:

 template <class T> struct non_deduced { using type = T; }; template <class T> using non_deduced_t = typename non_deduced<T>::type; template<class T, class Value> void Apply(void (T::*cb)(Value), T* obj, non_deduced_t<Value> v) { (obj->*cb)(v); } 

The last argument of v is of type non_deduced_t<Value> , which, as the name implies, is an non-deducible context. Therefore, during the process of template subtraction, Value is derived as int const& from the pointer to the member function (as before), and now we just connect it to the type for v .

Alternatively, you can choose cb as your own template parameter. At this point, Apply() just boils down to std::invoke() .

+12
source share

The expression f.GetValue() is a value l of type const int . When this is passed by value, the output of the template argument infers an int . In general, the output of Value from Value v will never produce a link or type with a top-level cv qualification.

Instead of Value you want to have two separate template parameters (one for the function type argument, one for the actual argument type) and use SFINAE to disable Apply when cb not called with v (or static_assert for a hard error).

+3
source share

All Articles