我们可以使用检测惯用法来检查一个类是否具有带特定签名的成员函数吗?

时间:2016-03-07 12:13:49

标签: c++ templates c++14 sfinae typetraits

给出({3}}

的(减少的)实现
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;
}

我们可以轻松检查类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。我知道有很多选项可以检查(成员)函数的完全签名。但我想知道,如果有可能修改此检测工具包以便检测foo::bar的参数类型正好是int&&

[我已经创建了此示例的detection idiom。]

3 个答案:

答案 0 :(得分:6)

不改变你的type_traits,你可以做

template<typename T, T> struct helper {};

template<class T>
using bar_t = decltype(helper<const int& (T::*)(int&&), &T::bar>{});

Demo

答案 1 :(得分:1)

改编replaceOne()dyp的想法,我想出了

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来电的返回类型。通过这种方式,我们与工具包保持一致。我们可以通过

来检测存在
static_assert(type_traits::is_detected_v<bar_t, foo, int&&>, "not detected");

然而,虽然这个解决方案完全按照我的意图行事,但我讨厌我需要编写&#34;如此复杂的代码&#34;对于我想要检测的每种方法。我已经问Jarod42针对此问题。

答案 2 :(得分:0)

我认为这不适用于检查const限定词。

decltype(std::declval<T>().bar(std::declval<Arguments>()...)) (T::*)(Arguments...)

始终会生成非常量函数指针类型,而如果&T::bar被标记为const,bar将生成常量函数指针。

这将导致尝试将const指针类型转换为非const指针类型以存储在integral_constant中的失败。