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

问题描述 投票:3回答:3

鉴于detection idiom的(减少)实施

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&&

[我已经创建了这个例子的live demo。]

c++ templates c++14 sfinae typetraits
3个回答
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
投票

适应了dypJarod42的想法,我想出了

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");

然而,虽然这个解决方案完全符合我的预期,但我讨厌我需要为我想要检测的每个方法编写“如此复杂的代码”。我问过针对这个问题的new question


0
投票

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

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

总是产生一个非常量函数指针类型,而如果&T::bar被标记为const,bar将产生一个const函数指针。

然后尝试将const指针类型转换为非常量指针类型以便在integral_constant中存储时失败。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.