C++ Templated function specialization with template - c++

I have a templated function meant to work with STL containers. However, I'd like to specialize this function for a templated class. How might I go about doing this? I'm stuck on C++11.
Edit: Updated code example. It appears to work when F is a POD like int, but is broken in the below code example where F is a class.
#include <vector>
#include <iostream>
template <class F>
class Temp {
private:
F f;
public:
Temp(F f) : f(f) {}
};
template <typename C, typename F>
struct MyClass {
MyClass (const C& c, const F& f) { }
operator C() const
{ return {}; }
};
// Base Template Function
template <typename C, typename F>
MyClass<C, F> operator| (const C & left, const F & right)
{
std::cout << "Generic version" << std::endl;
return MyClass<C, F>(left, right);
}
// Overload that I tried (ignored and used base template function instead)
template <typename C, typename F>
MyClass<C, F> operator| (const MyClass<C, F> & left, const F & right)
{
std::cout << "MyClass overload" << std::endl;
return MyClass<C, F>(C(left), right);
}
template <class F>
Temp<F> filter(F f) {
return Temp<F>(f);
}
int main ()
{
std::vector<int> v0 = { 1, 2, 3 };
auto m0 = v0
| filter([] (int i) {return i > 0;});
auto m1 = m0
| filter([] (int i) {return i < 3;});
return 0;
}
The above code example prints Generic version twice.

You can't partially specialize a template function.
But your overload should works.
I've modified your MyClass class to make it compile but, in the following example, the overload version is in charge.
#include <iostream>
template <typename C, typename F>
struct MyClass
{
template <typename ... Ts>
MyClass (Ts const & ...)
{ }
operator C() const
{ return {}; }
};
/* Base Template Function */
template <typename C, typename F>
MyClass<C, F> operator| (C const & left, F const & right)
{
std::cout << "Generic version" << std::endl;
return MyClass<C, F>(left, right);
}
/* Overload that I tried (ignored and used base template function instead) */
template <typename C, typename F>
MyClass<C, F> operator| (MyClass<C, F> const & left, F const & right)
{
std::cout << "MyClass overload" << std::endl;
return MyClass<C, F>(C(left), right);
}
int main ()
{
MyClass<int, int> m0;
auto x = m0 | 0;
static_assert( std::is_same_v<decltype(x), MyClass<int, int>>, "!" );
}

Related

How to use C++ template magic in order to pattern match on type

The desire is to have only one wrapper for all typename T which support structured bindings for example via tuple_size and tuple_element without runtime overhead(contexprs, SFINAE). There's a function encode with accepts T obj as an argument and calls encode_impl with more specific arguments and type arguments.
The article https://playfulprogramming.blogspot.com/2016/12/serializing-structs-with-c17-structured.html uses a bunch of arity functions to achieve the same result. But as far as I understand tuples provide std::tuple_size which is possible to utilize.
#include <tuple>
#include <utility>
class Aa {
public:
Aa(int a1_, int a2_): a1(a1_), a2(a2_) {}
template<std::size_t N>
decltype(auto) get() const {
if constexpr (N == 0) return a1;
else if constexpr (N == 1) return a2;
}
private:
int a1;
int a2;
};
class Bb {
public:
Bb(Aa a_, int b_): a(a_), b(b_) {}
template<std::size_t N>
decltype(auto) get() const {
if constexpr (N == 0) return a;
else if constexpr (N == 1) return b;
}
private:
Aa a;
int b;
};
namespace std {
// Aa
template<>
struct tuple_size<Aa> : std::integral_constant<std::size_t, 2> {};
template<std::size_t N>
struct tuple_element<N, Aa> {
using type = decltype(std::declval<Aa>().get<N>());
};
// Bb
template<>
struct tuple_size<Bb> : std::integral_constant<std::size_t, 2> {};
template<std::size_t N>
struct tuple_element<N, Bb> {
using type = decltype(std::declval<Bb>().get<N>());
};
}
template <size_t N>
using size = std::integral_constant<size_t, N>;
template<typename T>
void encode(T t) {
encode_impl<?std::tuple_size<T>?()>(T t, ?std::tuple_size<T>);
}
template<?>
encode_impl(T t, ?) {
std::cout << "It works";
}
The expectations of resolving the issue are the understanding of notation which must be used to compile the code snippet. Right now you can see ? in many places.
If it's not possible to do using std::tuple_size then alternative solutions are welcome. Arity function from the article for classes don't work but that's a bit different question.
Are you asking how to apply the return values of some_type::get<i>() as the arguments of a function?
Off the top of my head you would write encode and encode_impl something like this:
template<typename T>
void encode(T const& t, std::ostream& os)
{
encode_impl(t, os, std::make_index_sequence<std::tuple_size<T>::value>{ });
}
template
<typename T, std::size_t... I>
void encode_impl(T const& t, std::ostream& os, std::index_sequence<I...> const)
{
constexpr auto last = sizeof...(I) - 1;
os << "{ ";
[[maybe_unused]] int const temp[] =
{ ((os << t.template get<I>() << (I != last ? ", " : " ")), 0)... };
os << "}" << std::endl;
}
This disgusting looking pack expansion in encode_impl is using the comma operator just to force evaluation of its left hand operand and discard the result, then evaluate the literal 0 and store it in the dummy array temp. The pack expansion is used to initialize an array so that the arguments are evaluated in the correct order (left to right).
UPDATE:
Okay so I think what you want is to make your own type trait test is_tuple_like that returns true if the type T is "tuple-like" which just requires that the expression std::tuple_size<T>::value is well formed and the expression declval<T&>().template get<std::size_t(0)>() is well formed. From there you can write a function that prints the elements of "tuple-like" types and if any of those elements are tuple-like, print their elements, recursively. Here's what I came up with:
template
<
typename T,
typename tp_enabled =
std::void_t
<
decltype(std::tuple_size<T>::value),
decltype(std::declval<T&>().template get<std::size_t(0)>())
>
>
constexpr auto
is_tuple_like(int const)noexcept->bool
{
return true;
}
template
<typename T, typename tp_arg>
constexpr auto
is_tuple_like(tp_arg const)noexcept->bool
{
return false;
}
template<typename T>
auto encode(T const& t, std::ostream& os)->
std::enable_if_t<is_tuple_like<T>(0)>
{
encode_impl(t, os, std::make_index_sequence<std::tuple_size<T>::value>{ });
os << std::endl;
}
template
<bool is_last, typename T>
auto
encode_one(T const& t, std::ostream& os)->
std::enable_if_t<!is_tuple_like<T>(0)>
{
os << t << (is_last ? " " : ", ");
}
template
<bool is_last, typename T>
auto
encode_one(T const& t, std::ostream& os)->
std::enable_if_t<is_tuple_like<T>(0)>
{
encode_impl(t, os, std::make_index_sequence<std::tuple_size<T>::value>{ });
os << (is_last ? " " : ", ");
}
template
<typename T, std::size_t... I>
void encode_impl(T const& t, std::ostream& os, std::index_sequence<I...> const)
{
constexpr auto last = sizeof...(I) - 1;
os << "{ ";
[[maybe_unused]] int const temp[] =
{ (encode_one<I == last>(t.template get<I>(), os), 0)... };
os << "}";
}
int main () {
auto a = Aa(1, 1);
encode(a, std::cout);
auto b = Bb(a, 1);
encode(b, std::cout);
return 0;
}
OUTPUT:
{ 1, 1 }
{ { 1, 1 }, 1 }
UPDATE 2: So it turns out the above implementation of is_tuple_like compiles fine on GCC and the latest version of Clang (8.0.0) but fails to compile on Clang 7.0.0 so here is a version that works on Clang 7.0.0 it uses a variable template instead of a function template:
#include <tuple>
#include <utility>
#include <iostream>
class Aa {
public:
Aa(int a1_, int a2_): a1(a1_), a2(a2_) {}
template<std::size_t N>
decltype(auto) get() const {
if constexpr (N == 0) return a1;
else if constexpr (N == 1) return a2;
}
private:
int a1;
int a2;
};
class Bb {
public:
Bb(Aa a_, int b_): a(a_), b(b_) {}
template<std::size_t N>
decltype(auto) get() const {
if constexpr (N == 0) return a;
else if constexpr (N == 1) return b;
}
private:
Aa a;
int b;
};
namespace std {
// Aa
template<>
struct tuple_size<Aa> : std::integral_constant<std::size_t, 2> {};
template<std::size_t N>
struct tuple_element<N, Aa> {
using type = decltype(std::declval<Aa>().get<N>());
};
// Bb
template<>
struct tuple_size<Bb> : std::integral_constant<std::size_t, 2> {};
template<std::size_t N>
struct tuple_element<N, Bb> {
using type = decltype(std::declval<Bb>().get<N>());
};
}
template
<typename T, typename tp_enabled = std::void_t<>>
constexpr bool is_tuple_like = false;
template
<typename T>
constexpr bool
is_tuple_like
<
T,
std::void_t
<
decltype(std::tuple_size<T>::value),
decltype(std::declval<T&>().template get<std::size_t(0)>())
>
> = true;
template<typename T>
auto encode(T const& t, std::ostream& os)->
std::enable_if_t<is_tuple_like<T>>
{
encode_impl(t, os, std::make_index_sequence<std::tuple_size<T>::value>{ });
os << std::endl;
}
template
<bool is_last, typename T>
auto
encode_one(T const& t, std::ostream& os)->
std::enable_if_t<!is_tuple_like<T>>
{
os << t << (is_last ? " " : ", ");
}
template
<bool is_last, typename T>
auto
encode_one(T const& t, std::ostream& os)->
std::enable_if_t<is_tuple_like<T>>
{
encode_impl(t, os, std::make_index_sequence<std::tuple_size<T>::value>{ });
os << (is_last ? " " : ", ");
}
template
<typename T, std::size_t... I>
void encode_impl(T const& t, std::ostream& os, std::index_sequence<I...> const)
{
constexpr auto last = sizeof...(I) - 1;
os << "{ ";
[[maybe_unused]] int const temp[] =
{ (encode_one<I == last>(t.template get<I>(), os), 0)... };
os << "}";
}
int main () {
auto a = Aa(1, 1);
encode(a, std::cout);
auto b = Bb(a, 1);
encode(b, std::cout);
return 0;
}

C++ enable_if_t SFINAE

I am trying to understand why this piece is code isn't working as expected
#include <cstdio>
#include <vector>
#include <type_traits>
using namespace std;
struct Foo {
};
template<typename T, typename = void>
void compare(const T&a, const T&b) {
cout << "default" << endl;
}
template<typename T, std::enable_if_t<std::is_same<T, Foo>::value>>
void compare(const T& a, const T &b) {
cout << "In object" << endl;
}
int main(int argc, char const *argv[]) {
compare(1, 2);
{
vector<int> a, b;
compare(a, b);
}
{
Foo a, b;
compare(a, b);
}
return 0;
}
In all cases "default" is printed. For the last case I would expect that the 2nd function to get invoked.
You didn't specialize compare (it's impossible to partially specialize a function template anyway). Instead you provide an overload.
And the overload is always illegal:
Either enable_if_t is not defined.
Or it specifies a non-type template parameter of type void.
So it's never going to be called, because SFINAE discards it in favor of the always valid overload at the top.
Specialization is usually the wrong answer for function templates. Instead you should delegate to a class template, which will behave as expected upon true specialization:
template<typename T, typename = void>
struct compare_impl {
static void execute(T const& l, T const& r) { /*Base case code*/ }
};
template<typename T>
struct compare_impl<T, std::enable_if_t<std::is_same<T, Foo>::value>> {
static void execute(T const& l, T const& r) { /*Special case code*/ }
};
template<typename T>
void compare (T const& l, T const& r) { compare_impl<T>::execute(a, b); }
Using a class template is a valid solution, here's another way to achieve this result with tag dispatching:
template <class T>
void compare(const T & l, const T & r, std::true_type)
{
cout << "In object" << endl;
}
template <class T>
void compare(const T & l, const T & r, std::false_type)
{
cout << "default" << endl;
}
template <class T>
void compare(const T & l, const T & r)
{
compare(l, r, std::is_same<T, Foo>{});
}
Deferring to a specialised function object allows a lot of flexibility on how argument types and/or whether they are rvalues or lvalues.
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;
// general case
template<class T>
struct compare_impl
{
template<class U, class V>
auto operator()(U&& a, V&& b) const {
cout << "default" << endl;
}
};
// compare interface
template<class T, class U>
auto compare(T && a, U && b)
{
using ctype = std::common_type_t<std::decay_t<T>, std::decay_t<U>>;
auto impl = compare_impl<ctype>();
return impl(a, b);
}
// now specialise for objects for which we want custom behaviour
struct Foo {
};
template<>
struct compare_impl<Foo>
{
template<class U, class V>
auto operator()(U&& a, V&& b) const {
cout << "In object" << endl;
}
};
int main(int argc, char const *argv[]) {
compare(1, 2);
{
vector<int> a, b;
compare(a, b);
}
{
Foo a, b;
compare(a, b);
}
return 0;
}

Compile-time switch for mutable/immutable lambda

I am writing a class member function that will take a lambda with a given type T in the function argument. My question is: is it possible to overload the member function at compile-time based on the mutability of the argument? Below is the example:
// T is a given type for class.
template <typename T>
class Wrapper {
T _t;
// For T&
template <typename F, typename R = std::result_of_t<F(T&)>>
std::enable_if_t<std::is_same<R, void>::value> operator()(F&& f) {
f(_t);
}
// For const T&
template <typename F, typename R = std::result_of_t<F(const T&)>>
std::enable_if_t<std::is_same<R, void>::value> operator()(F&& f) const {
f(_t);
}
};
So, what I want is, if the give lambda is with the following signature, the first operator should be invoked.
[](T&) {
...
};
For constant argument, the second should be invoked.
[](const T&) {
}
If you plan to use non-capturing lambdas only, you can rely on the fact that they decay to pointers to functions.
It follows a minimal, working example:
#include<type_traits>
#include<iostream>
template <typename T>
class Wrapper {
T _t;
public:
auto operator()(void(*f)(T &)) {
std::cout << "T &" << std::endl;
return f(_t);
}
auto operator()(void(*f)(const T &)) const {
std::cout << "const T &" << std::endl;
return f(_t);
}
};
int main() {
Wrapper<int> w;
w([](int &){});
w([](const int &){});
}
Otherwise you can use two overloaded functions as it follows:
#include<type_traits>
#include<iostream>
#include<utility>
template <typename T>
class Wrapper {
T _t;
template<typename F>
auto operator()(int, F &&f)
-> decltype(std::forward<F>(f)(const_cast<const T &>(_t))) const {
std::cout << "const T &" << std::endl;
return std::forward<F>(f)(_t);
}
template<typename F>
auto operator()(char, F &&f) {
std::cout << "T &" << std::endl;
return std::forward<F>(f)(_t);
}
public:
template<typename F>
auto operator()(F &&f) {
return (*this)(0, std::forward<F>(f));
}
};
int main() {
Wrapper<int> w;
w([](int &){});
w([](const int &){});
}

How to call conditionally B::f only if derived from B in C++11?

In case when static polymorphism is used, especially in templates (e.g. with policy/strategy pattern), it may be required to call base function member, but you don't know was instantiated class actually derived from this base or not.
This easily can be solved with old good C++ ellipsis overload trick:
#include <iostream>
template <class I>
struct if_derived_from
{
template <void (I::*f)()>
static void call(I& x) { (x.*f)(); }
static void call(...) { }
};
struct A { void reset() { std::cout << "reset A" << std::endl; } };
struct B { void reset() { std::cout << "reset B" << std::endl; } };
struct C { void reset() { std::cout << "reset C" << std::endl; } };
struct E: C { void reset() { std::cout << "reset E" << std::endl; } };
struct D: E {};
struct X: A, D {};
int main()
{
X x;
if_derived_from<A>::call<&A::reset>(x);
if_derived_from<B>::call<&B::reset>(x);
if_derived_from<C>::call<&C::reset>(x);
if_derived_from<E>::call<&E::reset>(x);
return 0;
}
The question is:
Is there any better simple way (e.g. SFINAE doesn't look so) to achieve same result in C++11/C++14?
Would empty call of ellipsis parameter function be elided by optimizing compiler? Hope such case is not special against any "normal" function.
One option is to introduce two overloads of different priorities and to equip the preferred one with an expression SFINAE.
#include <utility>
template <typename T, typename... Args, typename C, typename R, typename... Params>
auto call_impl(int, R(C::*f)(Args...), T&& t, Params&&... params)
-> decltype((std::forward<T>(t).*f)(std::forward<Params>(params)...))
{
return (std::forward<T>(t).*f)(std::forward<Params>(params)...);
}
template <typename T, typename... Args, typename C, typename R, typename... Params>
void call_impl(char, R(C::*)(Args...), T&&, Params&&...)
{
}
template <typename T, typename... Args, typename C, typename R, typename... Params>
auto call(R(C::*f)(Args...), T&& t, Params&&... params)
-> decltype(call_impl(0, f, std::forward<T>(t), std::forward<Params>(params)...))
{
return call_impl(0, f, std::forward<T>(t), std::forward<Params>(params)...);
}
Test:
int main()
{
X x;
call(&B::reset, x);
}
DEMO
The upper function will be selected first by overload resolution (due to an exact match of 0 against int), and possibly excluded from the set of viable candidates if (t.*f)(params...) is not valid. In the latter case, the call to call_impl falls back to the second overload, which is a no-op.
Given that &A::reset may fail for multiple reasons, and you may not necessarily want to explicitly specify the function's signature, and, on top of that, you want the call to fail if the member function exists, but it does not match function call arguments, then you can exploit generic lambdas:
#include <utility>
#include <type_traits>
template <typename B, typename T, typename F
, std::enable_if_t<std::is_base_of<B, std::decay_t<T>>{}, int> = 0>
auto call(T&& t, F&& f)
-> decltype(std::forward<F>(f)(std::forward<T>(t)))
{
return std::forward<F>(f)(std::forward<T>(t));
}
template <typename B, typename T, typename F
, std::enable_if_t<!std::is_base_of<B, std::decay_t<T>>{}, int> = 0>
void call(T&& t, F&& f)
{
}
Test:
int main()
{
X x;
call<A>(x, [&](auto&& p) { return p.A::reset(); });
call<B>(x, [&](auto&& p) { return p.B::reset(); });
}
DEMO 2
what about something like:
#include <iostream>
#include <type_traits>
struct A { void reset() { std::cout << "reset A" << std::endl; } };
struct B { void reset() { std::cout << "reset B" << std::endl; } };
struct X :public A{};
template <typename T, typename R, typename BT>
typename std::enable_if<std::is_base_of<BT, T>::value, R>::type
call_if_possible(T & obj, R(BT::*mf)())
{
return (obj.*mf)();
}
template <typename T, typename R, typename BT>
typename std::enable_if<!std::is_base_of<BT, T>::value, R>::type
call_if_possible(T & obj, R(BT::*mf)()) { }
int main()
{
X x;
call_if_possible(x, &A::reset);
call_if_possible(x, &B::reset);
}
ideone
edit
maybe more readable way:
template <typename T, typename R, typename BT>
R call_if_possible_impl(T & obj, R(BT::*mf)(), std::false_type){}
template <typename T, typename R, typename BT>
R call_if_possible_impl(T & obj, R(BT::*mf)(), std::true_type)
{
return (obj.*mf)();
}
template <typename T, typename R, typename BT>
R call_if_possible(T & obj, R(BT::*mf)())
{
return call_if_possible_impl(obj, mf, typename std::is_base_of<BT, T>::type());
}
ideone
Basing on previously provided answers by #PiotrSkotnicki and #relaxxx I would like to combine the most simple and readable solution, without SFINAE and other blood-from-the-eyes things. It's just for reference, will not be accepted anyway:
#include <iostream>
#include <type_traits>
template <class Base, class Derived>
using check_base = typename std::is_base_of<Base, Derived>::type;
template <class Base, class Derived, typename Func>
void call(Derived& d, Func&& f)
{
call<Base>(d, std::forward<Func>(f), check_base<Base, Derived>());
}
template <class Base, typename Func>
void call(Base& b, Func&& f, std::true_type)
{
f(b);
}
template <class Base, class Derived, typename Func>
void call(Derived&, Func&&, std::false_type)
{
}
struct A { void reset(int i) { std::cout << "reset A: " << i << std::endl;} };
struct B { void reset() { std::cout << "reset B" << std::endl;} };
struct C { void reset() { std::cout << "reset C" << std::endl;} };
struct E: C { void reset() { std::cout << "reset E" << std::endl;} };
struct D: A, E {};
int main()
{
D d;
int i = 42;
call<A>(d, [&](auto& p) { p.reset(i); } );
call<B>(d, [](auto& p) { p.reset(); } );
call<C>(d, [](auto& p) { p.reset(); } );
call<E>(d, [](auto& p) { p.reset(); } );
}
Live at: http://cpp.sh/5tqa

c++0x: overloading on lambda arity

I'm trying to create a function which can be called with a lambda that takes either 0, 1 or 2 arguments. Since I need the code to work on both g++ 4.5 and vs2010(which doesn't support variadic templates or lambda conversions to function pointers) the only idea I've come up with is to choose which implementation to call based on arity. The below is my non working guess at how this should look. Is there any way to fix my code or is there a better way to do this in general?
#include <iostream>
#include <functional>
using namespace std;
template <class Func> struct arity;
template <class Func>
struct arity<Func()>{ static const int val = 0; };
template <class Func, class Arg1>
struct arity<Func(Arg1)>{ static const int val = 1; };
template <class Func, class Arg1, class Arg2>
struct arity<Func(Arg1,Arg2)>{ static const int val = 2; };
template<class F>
void bar(F f)
{
cout << arity<F>::val << endl;
}
int main()
{
bar([]{cout << "test" << endl;});
}
A lambda function is a class type with a single function call operator. You can thus detect the arity of that function call operator by taking its address and using overload resolution to select which function to call:
#include <iostream>
template<typename F,typename R>
void do_stuff(F& f,R (F::*mf)() const)
{
(f.*mf)();
}
template<typename F,typename R,typename A1>
void do_stuff(F& f,R (F::*mf)(A1) const)
{
(f.*mf)(99);
}
template<typename F,typename R,typename A1,typename A2>
void do_stuff(F& f,R (F::*mf)(A1,A2) const)
{
(f.*mf)(42,123);
}
template<typename F>
void do_stuff(F f)
{
do_stuff(f,&F::operator());
}
int main()
{
do_stuff([]{std::cout<<"no args"<<std::endl;});
do_stuff([](int a1){std::cout<<"1 args="<<a1<<std::endl;});
do_stuff([](int a1,int a2){std::cout<<"2 args="<<a1<<","<<a2<<std::endl;});
}
Be careful though: this won't work with function types, or class types that have more than one function call operator, or non-const function call operators.
I thought the following would work but it doesn't, I'm posting it for two reasons.
To save people the time if they had the same idea
If someone knows why this doesn't work, I'm not 100% sure I understand (although I have my suspicions)
Code follows:
#include <iostream>
#include <functional>
template <typename Ret>
unsigned arity(std::function<Ret()>) { return 0; }
template <typename Ret, typename A1>
unsigned arity(std::function<Ret(A1)>) { return 1; }
template <typename Ret, typename A1, typename A2>
unsigned arity(std::function<Ret(A1, A2)>) { return 2; }
// rinse and repeat
int main()
{
std::function<void(int)> f = [](int i) { }; // this binds fine
// Error: no matching function for call to 'arity(main()::<lambda(int)>)'
std::cout << arity([](int i) { });
}
Compile time means of obtaining the arity of a function or a function object, including that of a lambda:
int main (int argc, char ** argv) {
auto f0 = []() {};
auto f1 = [](int) {};
auto f2 = [](int, void *) {};
std::cout << Arity<decltype(f0)>::value << std::endl; // 0
std::cout << Arity<decltype(f1)>::value << std::endl; // 1
std::cout << Arity<decltype(f2)>::value << std::endl; // 2
std::cout << Arity<decltype(main)>::value << std::endl; // 2
}
template <typename Func>
class Arity {
private:
struct Any {
template <typename T>
operator T ();
};
template <typename T>
struct Id {
typedef T type;
};
template <size_t N>
struct Size {
enum { value = N };
};
template <typename F>
static Size<0> match (
F f,
decltype(f()) * = nullptr);
template <typename F>
static Size<1> match (
F f,
decltype(f(Any())) * = nullptr,
decltype(f(Any())) * = nullptr);
template <typename F>
static Size<2> match (
F f,
decltype(f(Any(), Any())) * = nullptr,
decltype(f(Any(), Any())) * = nullptr,
decltype(f(Any(), Any())) * = nullptr);
public:
enum { value = Id<decltype(match(static_cast<Func>(Any())))>::type::value };
};
This way works:
template<typename F>
auto call(F f) -> decltype(f(1))
{
return f(1);
}
template<typename F>
auto call(F f, void * fake = 0) -> decltype(f(2,3))
{
return f(2,3);
}
template<typename F>
auto call(F f, void * fake = 0, void * fake2 = 0) -> decltype(f(4,5,6))
{
return f(4,5,6);
}
int main()
{
auto x1 = call([](int a){ return a*10; });
auto x2 = call([](int a, int b){ return a*b; });
auto x3 = call([](int a, int b, int c){ return a*b*c; });
// x1 == 1*10
// x2 == 2*3
// x3 == 4*5*6
}
It works for all callable types (lambdas, functors, etc)