I'm trying to make a simply example for myself using new concept syntax. I decided to test if a type had operator() defined, and created a struct to test this using the SFINAE paradigm, but I'm running into type issues. Here is my code:
#include <utility>
#include <functional>
namespace Templates::Concepts {
template<class type__>
struct call_check {
template<class type_ = type__>
static auto check(std::nullptr_t) -> decltype(std::declval<type_>().operator()(), std::false_type(), std::true_type());
template<class type_ = type__>
static auto check(...) -> decltype(std::false_type());
template<class type_ = type__>
using type = decltype(check<type_>(nullptr));
};
template<typename type_>
concept bool Callable = []() -> bool { typename call_check<type_>::type *t; return *t;};
}
I started without the 'typename' pointer, and just had
return call_check<type_>::type;
,
but I received name-dependent type errors. After adding the typename I now receive
concepts.h:20:78: error: ‘typename Templates::Concepts::call_check<yes>::type’ names ‘template<class type_> using type = decltype (check<type_>(nullptr))’, which is not a type
,
and I'm stuck. To be frank, I'm not completely sure what the most correct way is to implement this SFINAE check, so I'm not sure where to start. Any help with the paradigm and/or with concepts would also be appreciated.
I did see an example with something along the lines of
std::declval<type_>()(std::declval<other>(), std::declval<op>()), ...
replacing the first item in the decltype call of the first check (for binary operators), but I had difficulty understanding how that translated to a function call. (Third answer from the top, for reference: How to check whether operator== exists?).
With C++20 concept you can avoid "verbose and ugly" SFINAE paradigm.
Disclaimer: the following code is Gnu Concepts compatible (C++20 concept is not implemented yet).
Let's define the following concept which checks operator()
existence on the type T
:
template <typename T>
concept bool Callable() {
return requires(T& t) {
{t()}
};
}
Now you can simply use it:
void bar(const Callable& t) {
t();
}
Another solution can be obtained simply with std::is_invocable
:
For example:
template <typename T>
struct Callable {
static constexpr bool value = std::is_invocable_v<T>;
};
This is C++17 compatible.