Reputation: 18228
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
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
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