检查一个类型是一个包含泛型 lambda 的函子
Check a type is a functor including generic lambda
我可以写一个 trait 元函数来判断一个类型是不是仿函数吗?
有大量代码可以使用 decltype(&T::operator())
的 SFINAE 检查函子,例如
template<class T>
struct is_functor {
template<class F>
static auto test(decltype(&F::operator())) -> std::true_type;
template<class F>
static auto test(...) -> std::false_type;
static constexpr bool value = decltype(test<T>(0))::value;
};
但是,这不适用于通用 lambda,因为通用 lambda
的 operator()
是一个模板函数。
通用 lambda 版本的有限情况下有一些代码对通用 lambda 的参数类型进行了一些限制。例如,如果 lambda 表达式包含任何对 int
类型无效的表达式(例如成员访问操作),则此处的答案 () 将不起作用。
我不需要任何通用性。事实上,我只需要知道一个类型可以是一个只接受一个参数的仿函数。
如何实现我的 is_functor
?
用例:
我正在尝试验证给定参数是否为模板函数的仿函数,也就是说,我想要一些重载的模板函数,例如:
template<class F, class = enable_if_t<is_functor<std::decay_t<F>>::value>>
auto make_func(F &&f) { return std::forward<F>(f); }
template<class F, class = enable_if_t<!is_functor<std::decay_t<F>>::value>>
auto make_func(F &&f) { return [f=std::forward<F>(f)] (auto&&) { return f; }; }
没有正确的方法来做到这一点(至少在我们得到静态反射之前)。你能做的最好的事情就是检查一个对象是否 callable 具有一定的置信度:
尝试获取其 operator()
地址。如果失败,则对象可能是 non-callable 或者它的 operator()
可能是 overloaded/templated.
尝试使用为常用函数提供接口的虚拟 any_type
实例调用对象。这可能会帮助您推断出它的数量。
如果一切都失败了,强制用户以某种方式帮助推导元数或手动指定元数。
解决这个问题的一种方法是使用一组 deduced_arity
标签:
namespace deduced_arity
{
template <std::size_t TS>
struct deducible_t : std::integral_constant<std::size_t, TS>
{
};
struct undeducible_t
{
};
constexpr undeducible_t undeducible{};
constexpr deducible_t<1> unary{};
}
您还需要某种 function_traits
实现,它将静态地告诉您 函数对象 的确切元数。这可以是 found in Boost.
那你还需要an implementation of any_type
.
之后,您可以使用类似于以下类型特征的东西来检查 函数对象 是否可以 被重载,使用检测成语:
template <typename T>
using is_not_overloaded_impl = decltype(&T::operator());
template <typename T>
using is_not_overloaded =
std::experimental::is_detected<is_not_overloaded_impl, T>;
然后你可以使用 if constexpr(...)
链(或任何其他 compile-time 分支机制) 来做出很好的猜测 - 例如:
if constexpr(is_not_overloaded<T>{})
{
// use `function_traits` here
}
else if constexpr(std::is_callable<T(any_type)>{})
{
return deduced_arity::unary;
}
else if constexpr(/* user manually marked arity */)
{
/* deal with user-defined deduction helpers */
}
else
{
return deduced_arity::undeducible;
}
重复使用 中的大部分代码(关于查找函数的元数的代码):
template <class, std::size_t N, class = std::make_index_sequence<N>, class = void_t<>>
struct CanCall : std::false_type { };
template <class F, std::size_t N, std::size_t... Idx>
struct CanCall<
F, N,
std::index_sequence<Idx...>,
void_t<decltype(std::declval<F>()((Idx, std::declval<Any const&&>())...))>
> : std::true_type { };
CanCall<F, N>
将 return F
是否可以使用 N
任意类型的参数调用。 Any 帮助器类型具有模板化的隐式转换运算符,允许它变形为任何所需的参数类型。
然后,对于您的特定 use-case(一元函子):
template <class F>
struct IsUnaryFunctor : CanCall<F, 1u> { };
我可以写一个 trait 元函数来判断一个类型是不是仿函数吗?
有大量代码可以使用 decltype(&T::operator())
的 SFINAE 检查函子,例如
template<class T>
struct is_functor {
template<class F>
static auto test(decltype(&F::operator())) -> std::true_type;
template<class F>
static auto test(...) -> std::false_type;
static constexpr bool value = decltype(test<T>(0))::value;
};
但是,这不适用于通用 lambda,因为通用 lambda
的 operator()
是一个模板函数。
通用 lambda 版本的有限情况下有一些代码对通用 lambda 的参数类型进行了一些限制。例如,如果 lambda 表达式包含任何对 int
类型无效的表达式(例如成员访问操作),则此处的答案 () 将不起作用。
我不需要任何通用性。事实上,我只需要知道一个类型可以是一个只接受一个参数的仿函数。
如何实现我的 is_functor
?
用例:
我正在尝试验证给定参数是否为模板函数的仿函数,也就是说,我想要一些重载的模板函数,例如:
template<class F, class = enable_if_t<is_functor<std::decay_t<F>>::value>>
auto make_func(F &&f) { return std::forward<F>(f); }
template<class F, class = enable_if_t<!is_functor<std::decay_t<F>>::value>>
auto make_func(F &&f) { return [f=std::forward<F>(f)] (auto&&) { return f; }; }
没有正确的方法来做到这一点(至少在我们得到静态反射之前)。你能做的最好的事情就是检查一个对象是否 callable 具有一定的置信度:
尝试获取其
operator()
地址。如果失败,则对象可能是 non-callable 或者它的operator()
可能是 overloaded/templated.尝试使用为常用函数提供接口的虚拟
any_type
实例调用对象。这可能会帮助您推断出它的数量。如果一切都失败了,强制用户以某种方式帮助推导元数或手动指定元数。
解决这个问题的一种方法是使用一组 deduced_arity
标签:
namespace deduced_arity
{
template <std::size_t TS>
struct deducible_t : std::integral_constant<std::size_t, TS>
{
};
struct undeducible_t
{
};
constexpr undeducible_t undeducible{};
constexpr deducible_t<1> unary{};
}
您还需要某种 function_traits
实现,它将静态地告诉您 函数对象 的确切元数。这可以是 found in Boost.
那你还需要an implementation of any_type
.
之后,您可以使用类似于以下类型特征的东西来检查 函数对象 是否可以 被重载,使用检测成语:
template <typename T>
using is_not_overloaded_impl = decltype(&T::operator());
template <typename T>
using is_not_overloaded =
std::experimental::is_detected<is_not_overloaded_impl, T>;
然后你可以使用 if constexpr(...)
链(或任何其他 compile-time 分支机制) 来做出很好的猜测 - 例如:
if constexpr(is_not_overloaded<T>{})
{
// use `function_traits` here
}
else if constexpr(std::is_callable<T(any_type)>{})
{
return deduced_arity::unary;
}
else if constexpr(/* user manually marked arity */)
{
/* deal with user-defined deduction helpers */
}
else
{
return deduced_arity::undeducible;
}
重复使用
template <class, std::size_t N, class = std::make_index_sequence<N>, class = void_t<>>
struct CanCall : std::false_type { };
template <class F, std::size_t N, std::size_t... Idx>
struct CanCall<
F, N,
std::index_sequence<Idx...>,
void_t<decltype(std::declval<F>()((Idx, std::declval<Any const&&>())...))>
> : std::true_type { };
CanCall<F, N>
将 return F
是否可以使用 N
任意类型的参数调用。 Any 帮助器类型具有模板化的隐式转换运算符,允许它变形为任何所需的参数类型。
然后,对于您的特定 use-case(一元函子):
template <class F>
struct IsUnaryFunctor : CanCall<F, 1u> { };