C++11:模板参数中的 SFINAE,GCC vs Clang

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

我想实现一个小特征-class 来确定一个类型是否已正确重载 operator(),这样我就可以像这样查询一个类型:

FunctorCheck<F, void(int, char)>::value

最初,我从 this question, but after seeing a Cppcon lecture on TMP 那里得到了一个关于如何实现它的想法,我突然意识到这个问题可以更优雅地解决。这是我想出的,它可以在 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
{};

您可能已经注意到,我正在使用建议的 C++17 void_t 在专业化的模板参数中利用 SFINAE。当 FunctorReturn 接收到与参数列表不兼容的 Functor 类型时,void_t<FunctorReturn<Functor, Args ...>> 将格式错误,SFINAE 将启动并且将实例化非专用版本。如果前一个表达式格式正确,std::is_same 比较 return 类型以确定我们是否匹配。

在前面提到的讲座中,Walter Brown 提到这项技术从第一天开始就在 Clang 上发挥作用,并且 从第一天起就没有在 GCC 上发挥作用,仅仅是因为他们选择了不同的标准未能指定的东西的实现。但是,考虑到这个版本 比我以前的版本 优雅得多,我能做些什么来在 GCC 上进行编译( >= 4.9)?

(另外,有没有人知道这在 Visual Studio 的最新版本上会如何表现?)

这是 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,并尝试实例化 class 模板特化,这会导致硬错误。

GCC (< 5.0) 的解决方法是以下实现:

template <typename...>
struct voider
{
    using type = void;
};

template <typename... Ts>
using void_t = typename voider<Ts...>::type;

DEMO