我正在尝试根据作为参数传递给它的lambda的优缺点来专门化模板化函数。这是我想出的解决方案:
template<typename Function, bool>
struct helper;
template<typename Function>
struct helper<Function, false>
{
auto operator()(Function&& func)
{
std::cout << "Called 2 argument version.\n";
return func(1, 2);
}
};
template<typename Function>
struct helper<Function, true>
{
auto operator()(Function&& func)
{
std::cout << "Called 3 argument version.\n";
return func(1, 2, 3);
}
};
template<typename T>
struct B
{
T a;
const T someVal() const { return a; }
};
template<typename Function, typename T>
auto higherOrderFun(Function&& func, const T& a)
{
return helper<Function, std::is_invocable<Function, decltype(a.someVal()), decltype(a.someVal()), decltype(a.someVal())>::value>{}(std::forward<Function>(func));
}
int main()
{
B<int> b;
std::cout << higherOrderFun([](auto x, auto y) {return x+y; }, b) << "\n";
std::cout << higherOrderFun([](auto x, auto y, auto z) {return x + y+z; }, b) << "\n";
return 0;
}
有没有一种方法可以更优雅地实现这一目标?我已经浏览过:Arity of a generic lambda
但是,最新的解决方案(florestan的)将所有参数转换为aribtrary_t
,因此必须将它们强制转换回每个lambda内,我认为并不理想。理想情况下,我希望直接使用SFINAE对模板化的higherOrderFun
进行专用化,但是由于它是我使用的帮助器类来实现这一点。还有更简单的方法吗?例如将SFINAE直接应用于higherOrderFun
而不依赖于helper
类吗?这样做的全部目的是不必将higherOrderFun
更改为higherOrderFun2
和higherOrderFun3
,而是让编译器根据lambda和给定的参数({{1} }。
我应该提到的是,我也不在乎函数参数的类型-只是在乎它们的数量,因此如果我将示例中的const T& a
更改为decltype(a.someVal())
,可能(也许有一种规避显式定义类型的方法?)。
答案 0 :(得分:3)
以下模板为我提供了lambda,std::function
或普通函数指针的参数数量。这似乎涵盖了所有基础知识。因此,您专门研究n_lambda_parameters<T>::n
,并将其插入模板。根据您的特定用例,您可能需要使用std::remove_reference_t
或std::decay_t
提供的功能来包装它。
使用g ++ 9进行了测试。C++ 17要求std::void_t
,在其他地方可以找到许多模拟std::void_t
pre C ++ 17的示例...
#include <functional>
// Plain function pointer.
template<typename T> struct n_func_parameters;
template<typename T, typename ...Args>
struct n_func_parameters<T(Args...)> {
static constexpr size_t n=sizeof...(Args);
};
// Helper wrapper to tease out lambda operator()'s type.
// Tease out closure's operator()...
template<typename T, typename> struct n_extract_callable_parameters;
// ... Non-mutable closure
template<typename T, typename ret, typename ...Args>
struct n_extract_callable_parameters<T, ret (T::*)(Args...) const> {
static constexpr size_t n=sizeof...(Args);
};
// ... Mutable closure
template<typename T, typename ret, typename ...Args>
struct n_extract_callable_parameters<T, ret (T::*)(Args...)> {
static constexpr size_t n=sizeof...(Args);
};
// Handle closures, SFINAE fallback to plain function pointers.
template<typename T, typename=void> struct n_lambda_parameters
: n_func_parameters<T> {};
template<typename T>
struct n_lambda_parameters<T, std::void_t<decltype(&T::operator())>>
: n_extract_callable_parameters<T, decltype(&T::operator())> {};
#include <iostream>
void foo(int, char, double=0)
{
}
int main()
{
auto closure=
[](int x, int y)
// With or without mutable, here.
{
};
std::cout << n_lambda_parameters<decltype(closure)>::n
<< std::endl; // Prints 2.
std::cout << n_lambda_parameters<decltype(foo)>::n
<< std::endl; // Prints 3.
std::cout << n_lambda_parameters<std::function<void (int)>>::n
<< std::endl; // Prints 1.
return 0;
}
答案 1 :(得分:2)
我将使用不同的重载:
template<typename Function>
auto higherOrderFun(Function&& func)
-> decltype(std::forward<Function>(func)(1, 2, 3))
{
return std::forward<Function>(func)(1, 2, 3);
}
template<typename Function>
auto higherOrderFun(Function&& func)
-> decltype(std::forward<Function>(func)(1, 2))
{
return std::forward<Function>(func)(1, 2);
}
可能具有过载优先级
struct low_priority {};
struct high_priority : low_priority{};
template<typename Function>
auto higherOrderFunImpl(Function&& func, low_priority)
-> decltype(std::forward<Function>(func)(1, 2))
{
return std::forward<Function>(func)(1, 2);
}
template<typename Function>
auto higherOrderFunImpl(Function&& func, high_priority)
-> decltype(std::forward<Function>(func)(1, 2))
{
return std::forward<Function>(func)(1, 2);
}
template<typename Function>
auto higherOrderFun(Function&& func)
-> decltype(higherOrderFun(std::forward<Function>(func), high_priority{}))
{
return higherOrderFun(std::forward<Function>(func), high_priority{});
}
如果要使用arity traits from florestan,可能会导致:
template<typename F>
decltype(auto) higherOrderFun(F&& func)
{
if constexpr (arity_v<std::decay_t<F>, MaxArity> == 3)
{
return std::forward<F>(func)(1, 2, 3);
}
else if constexpr (arity_v<std::decay_t<F>, MaxArity> == 2)
{
return std::forward<F>(func)(1, 2);
}
// ...
}