也许不是一个完美的解决方案......但你可以尝试passepartout
struct passepartout
{
template <typename T>
operator T & ();
template <typename T>
operator T && ();
};
请注意,转换运算符仅被声明,未定义;所以这个结构可以decltype()
和std::declval()
(and std::is_invocable
) 一起使用,但不能被实例化。
现在您可以将higherOrderFunc
传递引用写入passepartout
to std::is_invocable
。
template <typename F,
std::enable_if_t<
std::is_invocable_v<F, passepartout &>
&& ! std::is_invocable_v<F, passepartout &, passepartout &>, bool>
= true>
void higherOrderFunc (F)
{ std::cout << "-- one parameter callable" << std::endl; }
template <typename F,
std::enable_if_t<
std::is_invocable_v<F, passepartout &, passepartout &>, bool> = true>
void higherOrderFunc (F)
{ std::cout << "-- two parameter callable" << std::endl; }
诀窍是,如果可调用对象等待auto
(或auto &
,或auto &&
),则类型被推断为passepartout
自身;当可调用等待特定类型(int
在以下示例中,有或没有引用)时,模板operator T & ()
(或operator T && ()
根据情况而定)与预期类型兼容(在某种意义上)。
下面是一个完整的编译示例
#include <type_traits>
#include <iostream>
struct passepartout
{
template <typename T>
operator T & ();
template <typename T>
operator T && ();
};
template <typename F,
std::enable_if_t<
std::is_invocable_v<F, passepartout &>
&& ! std::is_invocable_v<F, passepartout &, passepartout &>, bool>
= true>
void higherOrderFunc (F)
{ std::cout << "-- one parameter callable" << std::endl; }
template <typename F,
std::enable_if_t<
std::is_invocable_v<F, passepartout &, passepartout &>, bool> = true>
void higherOrderFunc (F)
{ std::cout << "-- two parameter callable" << std::endl; }
int main ()
{
auto l1a = [](auto &&){};
auto l1b = [](int &){};
auto l2a = [](auto &, int &&){};
auto l2b = [](auto, int const &){};
auto l2c = [](auto &&, auto const &){};
auto l2d = [](int &&, auto const &, auto && ...){};
higherOrderFunc(l1a);
higherOrderFunc(l1b);
higherOrderFunc(l2a);
higherOrderFunc(l2b);
higherOrderFunc(l2c);
higherOrderFunc(l2c);
higherOrderFunc(l2d);
}