Reputation: 103
How can I check the existence of a template function like this: Checking if reader
struct has read
arithmetic value
struct reader {
template<typename T>
std::enable_if_t<std::is_arithmetic<T>::value, T> read() {
return {};
}
};
I use a checker like this:
template <typename T>
struct test_read {
static constexpr auto value = std::is_convertible<decltype(std::declval<T>().read<int>()),int>::value;
};
But the compiler complains:
error: wrong number of template arguments (1, should be 2)
static constexpr auto value = std::is_convertible<decltype(std::declval<T>().read<int>()),int>::value;
Please give me your advice on that.
Thank you.
Update: Here is the final version I got after discussion, I hope everyone will find it helpful for your code
struct not_reader {
};
struct reader {
template<typename T>
std::enable_if_t<std::is_arithmetic<T>::value, T> read() {
return {};
}
};
template<class T, class Elem>
struct has_read {
private:
template<class C, typename=void>
struct test_read : std::false_type {
};
template<class C>
struct test_read<C, typename std::enable_if<std::is_convertible<decltype(std::declval<C>().template read<Elem>()), Elem>::value>::type>
: std::true_type {
};
public:
using type = typename test_read<T>::type;
static constexpr bool value = test_read<T>::value;
};
static_assert(has_read<reader, int>::value, "reader should have int read()");
static_assert(!has_read<not_reader, int>::value, "not_reader should not have int read()");
Upvotes: 4
Views: 267
Reputation: 41090
To briefly restate your problem in terms that are a bit clearer:
T
if T
satisfies is_arithmetic
, otherwise it returns void
int
will return a type convertible to int
I think the shortest path to fix your code is to take advantage of std::result_of
(C++11/14, use std::invoke_result_t
in C++17):
template<class T>
struct test_read {
static constexpr auto value = std::is_convertible<
typename std::result_of<decltype(&T::template read<int>)(T)>::type, int
>::value;
};
Some notes about this solution:
read
member function of T
(reader
), we need to use the template
keyword to inform the compiler that the name reader
is a template.result_of
requires a function-like syntax of F(Args)
, so here we are getting the type of reader::read
as the F
portion, and then passing reader
as the Args
portion
T
(reader)
to read
because it is a member function (rather than static
or free), and member functions implicitly take a reference to the instance of the class they're being called on.Upvotes: 1
Reputation: 66200
You forgot template
before read()
static constexpr auto value
= std::is_convertible<
decltype(std::declval<T>().template read<int>()),int>::value;
// .................................#########
But I don't think that your code can check " if reader
struct has read
arithmetic value": try calling test_read
with type int
and you should get a compilation error.
The following is an example of an alternative solution
#include <type_traits>
struct reader
{
template<typename T>
std::enable_if_t<std::is_arithmetic<T>::value, T> read()
{ return {}; }
};
template <typename, typename = void>
struct readTypeRet
{ using type = void; };
template <typename T>
struct readTypeRet<T, decltype(std::declval<T>().template read<int>(), void())>
{ using type = decltype(std::declval<T>().template read<int>()); };
template <typename T>
struct test_read
: public std::is_convertible<typename readTypeRet<T>::type, int>
{ };
int main ()
{
static_assert(test_read<reader>::value == true, "!");
static_assert(test_read<int>::value == false, "!");
}
Upvotes: 4