The goal of orElse<v.get(),0>() is clear enough, but if such a thing can exist, it must be one of:
Invocation Lineup
orElse(v,&V::get,0) orElse<V,&V::get>(v,0) orElse<V,&V::get,0>(v)
where v is of type v , and the function template created in this way will be respectively:
Functional Template
template<typename T> int orElse(T & obj, int(T::pmf*)(), int deflt); template<typename T, int(T::*)()> int orElse(T & obj, int deflt); template<typename T, int(T::*)(), int Default> int orElse(T & obj);
As you know, such a thing cannot exist with the desired effect.
For anyone who doesn't understand this, the reason is this: none of the functions in the Invocation Lineup will compile unless there is a member like V::get . This is not enough, and the fact that the called function can be instantiated in the Function Template Lineup does not matter. If V::get does not exist, then any code that mentions it will not compile.
However, you seem to have a practical goal, which you do not need to approach in this is simply hopeless. It looks like for a given name foo and a given type R , you want to write only one function template:
template<typename T, typename ...Args> R foo(T && obj, Args &&... args);
which will return the value of R(T::foo) by calling obj with arguments args... if such a member function exists and otherwise returns the default value of R
If this is correct, this can be achieved in accordance with the following illustration:
#include <utility> #include <type_traits> namespace detail { template<typename T> T default_ctor() { return T(); } // SFINAE `R(T::get)` exists template<typename T, typename R, R(Default)(), typename ...Args> auto get_or_default( T && obj, Args &&... args) -> std::enable_if_t< std::is_same<R,decltype(obj.get(std::forward<Args>(args)...)) >::value,R> { return obj.get(std::forward<Args>(args)...); } // SFINAE `R(T::get)` does not exist template<typename T, typename R, R(Default)(), typename ...Args> R get_or_default(...) { return Default(); } } //namespace detail // This is your universal `int get(T,Args...)` template<typename T, typename ...Args> int get(T && obj, Args &&... args) { return detail::get_or_default<T&,int,detail::default_ctor> (obj,std::forward<Args>(args)...); } // C++14, trivially adaptable for C++11
which can be checked with:
In the complex inverse type there is a "compound SFINAE":
std::enable_if_t< std::is_same<R,decltype(obj.get(std::forward<Args>(args)...)) >::value,R>
If T::get does not exist, then decltype(obj.get(std::forward<Args>(args)...) does not compile. But if it compiles and the return type T::get is something other than R , then the std::enable_if_t type specifier is not compiled. Only if there is a member function and has the desired return type R you can create an instance of R(T::get) . Otherwise, catch-all R(T::get) does not exist . case selected.
Note that get(aconst) returns 0, not 1. That should be because the non-constant overload of A::get() cannot be called on const A
You can use the same template for any other R foo(V & v,Args...) and existing or non-existent R(V::foo)(Args...) . If R not constructive by default, or if you want R to default, then it returns when R(V::foo) does not exist to be something other than R() , then define the function detail::fallback (or what something else), which returns the desired default value of R and specify it instead of detail::default_ctor
How nice would it be that you could once again template a template to accommodate any possible member function of T with any possible return type R But the additional template parameter that you will need for this is R(T::*)(typename...) , and its value for creation should be &V::get (or something else), and then the template forces you to mention in a fatal trap a thing whose existence is in doubt.