I want to implement a little trait-class to determine if a type has overloaded operator()
properly, so that I can query a type like so:
FunctorCheck<F, void(int, char)>::value
Originally, I got an idea on how to implement this from this question, but after seeing a Cppcon lecture on TMP, it dawned on me that this problem could be solved much more elegantly. This is what I came up with, and this compiles and runs flawlessly on Clang 3.5.0:
template <typename ...>
using void_t = void;
template <typename Functor, typename ... Args>
using FunctorReturn = decltype(declval<Functor>()(declval<Args>()...));
template <typename Functor, typename Signature, typename = void>
struct FunctorCheck: public std::false_type
{};
template <typename Functor, typename R, typename ... Args>
struct FunctorCheck<Functor, R(Args...),
void_t<FunctorReturn<Functor, Args ...>> // SFINAE can kick in here
> : public std::is_same<R, FunctorReturn<Functor, Args ...>>::type
{};
As you might have noticed, I'm using the proposed C++17 void_t
to exploit SFINAE in the template parameters of the specialization. When FunctorReturn
receives a Functor
-type that is incompatible with the argument-list, void_t<FunctorReturn<Functor, Args ...>>
will be ill-formed, SFINAE will kick in and the non-specialized version will be instantiated. If the former expression is well-formed, std::is_same
compares the return-types to determine if we have a match.
In the lecture mentioned before, Walter Brown mentions that this technique has worked from day 1 on Clang, and has not worked on GCC from day 1, simply because they chose different implementations on something the standard failed to specify. However, given that this version is so much more elegant than what I had before, is there anything I can do to make this compile on GCC (>= 4.9)?
(Also, does anyone have any clue as to how this would behave on recent versions of Visual Studio?)
This was CWG issue 1558. The unspecified part was treatment of unused arguments in an alias template. In GCC < 5.0 the unused arguments can't result in a substitution failure, hence void_t
fails to verify your functor call, and tries to instantiate a class template specialization, which results in a hard error.
A workaround for GCC (< 5.0) is the following implementation:
template <typename...>
struct voider
{
using type = void;
};
template <typename... Ts>
using void_t = typename voider<Ts...>::type;
DEMO
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With