Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11: SFINAE in template parameters, GCC vs Clang [duplicate]

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?)

like image 653
JorenHeit Avatar asked Feb 02 '15 16:02

JorenHeit


1 Answers

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

like image 137
Piotr Skotnicki Avatar answered Oct 10 '22 19:10

Piotr Skotnicki