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

为君一笑 提交于 2019-11-27 07:49:16

问题


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


回答1:


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



来源:https://stackoverflow.com/questions/28282398/c11-sfinae-in-template-parameters-gcc-vs-clang

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!