我们可以使用检测习惯用法来检查 class 是否具有具有特定签名的成员函数吗?
Can we use the detection idiom to check if a class has a member function with a specific signature?
的(简化)实施
namespace type_traits
{
template<typename... Ts>
using void_t = void;
namespace detail
{
template<typename, template<typename...> class, typename...>
struct is_detected : std::false_type {};
template<template<class...> class Operation, typename... Arguments>
struct is_detected<void_t<Operation<Arguments...>>, Operation, Arguments...> : std::true_type {};
}
template<template<class...> class Operation, typename... Arguments>
using is_detected = detail::is_detected<void_t<>, Operation, Arguments...>;
template<template<class...> class Operation, typename... Arguments>
constexpr bool is_detected_v = detail::is_detected<void_t<>, Operation, Arguments...>::value;
}
我们可以轻松检查 class foo
是否包含成员函数 bar
struct foo {
int const& bar(int&&) { return 0; }
};
template<class T>
using bar_t = decltype(std::declval<T>().bar(0));
int main()
{
static_assert(type_traits::is_detected_v<bar_t, foo>, "not detected");
return 0;
}
但是,如您所见,我们无法检测到 foo::bar
的参数类型是 int&&
。检测成功,导致0
可以传给foo::bar
。我知道有很多选项可以检查(成员)函数的 exact 签名。但我想知道,是否可以修改此检测工具包以检测 foo::bar
的参数类型恰好是 int&&
.
[我已经创建了这个示例的 live demo。]
不改变你的type_traits,你可以
template<typename T, T> struct helper {};
template<class T>
using bar_t = decltype(helper<const int& (T::*)(int&&), &T::bar>{});
借鉴dyp and 的思路,我想出了
template<class T, typename... Arguments>
using bar_t = std::conditional_t<
true,
decltype(std::declval<T>().bar(std::declval<Arguments>()...)),
std::integral_constant<
decltype(std::declval<T>().bar(std::declval<Arguments>()...)) (T::*)(Arguments...),
&T::bar
>
>;
请注意,bar_t
将是 bar
调用的 return 类型。通过这种方式,我们与工具包保持一致。我们可以通过
检测存在
static_assert(type_traits::is_detected_v<bar_t, foo, int&&>, "not detected");
然而,虽然这个解决方案完全符合我的预期,但我讨厌我需要为我想要检测的每个方法编写 "so much complicated code"。我已经针对这个问题提出了 new question。
我认为这不适用于检查 const 限定符。
decltype(std::declval<T>().bar(std::declval<Arguments>()...)) (T::*)(Arguments...)
总是生成一个非常量函数指针类型,而如果 bar
被标记为 const,&T::bar
将生成一个 const 函数指针。
这将无法尝试将常量指针类型转换为非常量指针类型以存储在 integral_constant
。
namespace type_traits
{
template<typename... Ts>
using void_t = void;
namespace detail
{
template<typename, template<typename...> class, typename...>
struct is_detected : std::false_type {};
template<template<class...> class Operation, typename... Arguments>
struct is_detected<void_t<Operation<Arguments...>>, Operation, Arguments...> : std::true_type {};
}
template<template<class...> class Operation, typename... Arguments>
using is_detected = detail::is_detected<void_t<>, Operation, Arguments...>;
template<template<class...> class Operation, typename... Arguments>
constexpr bool is_detected_v = detail::is_detected<void_t<>, Operation, Arguments...>::value;
}
我们可以轻松检查 class foo
是否包含成员函数 bar
struct foo {
int const& bar(int&&) { return 0; }
};
template<class T>
using bar_t = decltype(std::declval<T>().bar(0));
int main()
{
static_assert(type_traits::is_detected_v<bar_t, foo>, "not detected");
return 0;
}
但是,如您所见,我们无法检测到 foo::bar
的参数类型是 int&&
。检测成功,导致0
可以传给foo::bar
。我知道有很多选项可以检查(成员)函数的 exact 签名。但我想知道,是否可以修改此检测工具包以检测 foo::bar
的参数类型恰好是 int&&
.
[我已经创建了这个示例的 live demo。]
不改变你的type_traits,你可以
template<typename T, T> struct helper {};
template<class T>
using bar_t = decltype(helper<const int& (T::*)(int&&), &T::bar>{});
借鉴dyp and
template<class T, typename... Arguments>
using bar_t = std::conditional_t<
true,
decltype(std::declval<T>().bar(std::declval<Arguments>()...)),
std::integral_constant<
decltype(std::declval<T>().bar(std::declval<Arguments>()...)) (T::*)(Arguments...),
&T::bar
>
>;
请注意,bar_t
将是 bar
调用的 return 类型。通过这种方式,我们与工具包保持一致。我们可以通过
static_assert(type_traits::is_detected_v<bar_t, foo, int&&>, "not detected");
然而,虽然这个解决方案完全符合我的预期,但我讨厌我需要为我想要检测的每个方法编写 "so much complicated code"。我已经针对这个问题提出了 new question。
我认为这不适用于检查 const 限定符。
decltype(std::declval<T>().bar(std::declval<Arguments>()...)) (T::*)(Arguments...)
总是生成一个非常量函数指针类型,而如果 bar
被标记为 const,&T::bar
将生成一个 const 函数指针。
这将无法尝试将常量指针类型转换为非常量指针类型以存储在 integral_constant
。