is_invocable 具有任意函数参数类型
is_invocable with arbitrary function argument types
有没有办法将 std::is_invocable
与任意函数参数类型一起使用,例如:std::is_invocable<Function, auto>
。这个想法是检查 Function
是否可以接受 1 个参数,而不管参数的类型。对于一个用例,考虑两个 lambda:auto lambda1 = [](auto x) {...}
、auto lambda2 = [](auto x, auto y) {...}
和一个高阶模板函数:
// specialize for 1 argument
template<typename Function, std::enable_if_t<(std::is_invocable<Function, auto>::value && !std::is_invocable<Function, auto, auto>::value)>, bool> = true>
void higherOrderFunc(Function&& func);
// specialize for 2 arguments
template<typename Function, std::enable_if_t<std::is_invocable<Function, auto, auto>::value, bool> = true>
void higherOrderFunc(Function&& func);
第一种情况下的!std::is_invocable<Function, auto, auto>::value
是为了防止重载函数的歧义(也就是说,在这种情况下,首选的特化是第一个参数,以防出现歧义)。
请注意,我知道 auto
在这种情况下不能这样使用。我在问是否有办法实现这种行为(至少部分实现)。
也许不是一个完美的解决方案...但您可以尝试 passepartout
struct passepartout
{
template <typename T>
operator T & ();
template <typename T>
operator T && ();
};
注意转换运算符只是声明的,没有定义;所以这个结构可以在decltype()
和std::declval()
(和std::is_invocable
)中使用,但不能实例化。
现在您可以将对 passepartout
的 higherOrderFunc
传递引用写到 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);
}
有没有办法将 std::is_invocable
与任意函数参数类型一起使用,例如:std::is_invocable<Function, auto>
。这个想法是检查 Function
是否可以接受 1 个参数,而不管参数的类型。对于一个用例,考虑两个 lambda:auto lambda1 = [](auto x) {...}
、auto lambda2 = [](auto x, auto y) {...}
和一个高阶模板函数:
// specialize for 1 argument
template<typename Function, std::enable_if_t<(std::is_invocable<Function, auto>::value && !std::is_invocable<Function, auto, auto>::value)>, bool> = true>
void higherOrderFunc(Function&& func);
// specialize for 2 arguments
template<typename Function, std::enable_if_t<std::is_invocable<Function, auto, auto>::value, bool> = true>
void higherOrderFunc(Function&& func);
第一种情况下的!std::is_invocable<Function, auto, auto>::value
是为了防止重载函数的歧义(也就是说,在这种情况下,首选的特化是第一个参数,以防出现歧义)。
请注意,我知道 auto
在这种情况下不能这样使用。我在问是否有办法实现这种行为(至少部分实现)。
也许不是一个完美的解决方案...但您可以尝试 passepartout
struct passepartout
{
template <typename T>
operator T & ();
template <typename T>
operator T && ();
};
注意转换运算符只是声明的,没有定义;所以这个结构可以在decltype()
和std::declval()
(和std::is_invocable
)中使用,但不能实例化。
现在您可以将对 passepartout
的 higherOrderFunc
传递引用写到 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);
}