0xbadf00d
0xbadf00d

Reputation: 18228

Can we use the detection idiom to check if a class has a member function with a specific signature?

Given a (reduced) implementation of the 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;
}

we can easily check if a class foo contains a member function 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;
}

However, as you can see, we cannot detect that foo::bar's argument type is int&&. The detection succeeds, cause 0 can be passed to foo::bar. I know that there are plenty of options to check for the exact signature of a (member) function. But I would like to know, if it's possible to modify this detection toolkit in order to detect that foo::bar's argument type is exactly int&&.

[I've created a live demo of this example.]

Upvotes: 3

Views: 2221

Answers (3)

paulgessinger
paulgessinger

Reputation: 163

I don't think this works for checking const qualifiers.

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

always produces a non-const function pointer type, whereas &T::bar will produce a const function pointer if bar is marked const.

This will then fail trying to convert the const pointer type to the non-const pointer type for storage in integral_constant.

Upvotes: 0

0xbadf00d
0xbadf00d

Reputation: 18228

Adapting the ideas of dyp and Jarod42, I've came up with

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

Notice that bar_t will be the return type of a bar call. In this way, we stay consistent with the toolkit. We can detect the existence by

static_assert(type_traits::is_detected_v<bar_t, foo, int&&>, "not detected");

However, while this solution does exactly what I intended, I hate that I need to write "so much complicated code" for every method I want to detect. I've asked a new question targeting this issue.

Upvotes: 1

Jarod42
Jarod42

Reputation: 218098

Without changing your type_traits, you may do

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

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

Demo

Upvotes: 7

Related Questions