Is it possible to base template specialization on the presence or absence of a specific argument in a method signature?

Consider the following class:

class MyClass { public: template<class T> typename T::result_type apply(T& func) { if (is_int()) { return func(int(0)); } return func(double(0)); } ... }; 

(The code doesn't look terribly useful, but it's just a contrived sample to demonstrate my point)

Anyway, a typical functor would be something like this:

 struct MyFunc { typedef void result_type; template<class V> void operator()(V) { // do something } }; 

And you can use it like this:

 MyClass c; MyFunc f; c.apply(f); 

My question is this: is it possible to modify MyClass::apply to recognize a slightly different version of the functor in addition to the original one , for example, one that expects a reference to the object of the calling object to be passed along with all other parameters, something like this:

 struct MyFuncEx { typedef void result_type; template<class V> void operator()(const MyClass& caller, V) { // do something } }; 

So, the following code compiled too:

 MyClass c; MyFunc f; c.apply(f); MyFuncEx f2; c.apply(f2); 

As a bonus, I would like the compilation to fail if the functor contains both overloads, i.e. The following should complete with compilation:

 struct MyFuncSmartAss { typedef void result_type; template<class V> void operator()(V) { // do something } template<class V> void operator()(const MyClass& caller, V) { // do something } }; ... MyClass c; c.apply(MyFuncSmartAss()); 

But this is not so important if a longer overload takes precedence over a shorter one.

+3
source share
1 answer

It really depends on whether you have C ++ 11 or not. This should solve the overload problem in C ++ 11 (*):

 class MyClass { private: int _int; double _double; public: template <typename F> auto apply(F& f) -> decltype(f(_int), f(_double)) { if (is_int()) { return f(_int); } return f(_double); } template <typename F> auto apply(F& f) -> decltype(f(*this, _int), f(*this, _double)) { if (is_int()) { return f(*this, _double); } return f(_double) } }; 

How it works?

  • Removing unsuitable overloads: the trailing-return-type specification with decltype creates an unreasonable context. The compiler does regular overloading of the expression, but only takes care of the type. If an error occurs ( operator() does not exist in f ), we end up in SFINAE, and this apply overload is discarded
  • Ambiguity if both work: if both overloads are suitable (since f provides both operators), then the call is ambiguous

(*) I'm not too sure about the specification of the trailing-return-type type and, more specifically, the use of this and _arg . Both Clang 3.0 and gcc 4.5.2 error. You can get around this, just a little more.

 // first decltype(f(0), f(0.0)) // second decltype(f(std::declval<MyClass>(), 0), f(std::declval<MyClass>(), 0.0)) 

In action on ideon with this workaround:

EDIT: fulfill the int/double request.

+3
source

Source: https://habr.com/ru/post/906939/


All Articles