在 SFINAE 上下文中作为模板模板参数传递的引用类型的别名模板

Alias template for reference type passed as template template argument in SFINAE context

我在使用 G++ 6.1.0(-std=c++14 开关)时遇到了以下问题,我不明白为什么编译器会拒绝该代码。

我有一个辅助结构 is_well_formed,它在将另一个提供的类型替换到其中时检查提供的模板模板参数是否格式正确:

template<template<typename> typename R, typename T, typename = void>
struct is_well_formed : std::false_type {};

template<template<typename> typename R, typename T>
struct is_well_formed<R, T, void_t<R<T>>> : std::true_type {};

我想检查一个类型是否可引用。所以我的想法是写以下内容:

// (#1)
template<class T>
using reference_t = T&;

static_assert(!is_well_formed<reference_t, void>::value, "Reference to void!?");

但是我得到一个编译器错误:

main.cpp: In instantiation of 'struct is_well_formed<reference_t, double>':
main.cpp:62:51:   required from here
main.cpp:54:20: error: type/value mismatch at argument 1 in template parameter list for 'template<template<class> class R, class T, class> struct is_well_formed'
  : std::false_type {};
                    ^
main.cpp:54:20: note:   expected a class template, got 'reference_t'
main.cpp:54:20: error: type/value mismatch at argument 1 in template parameter list for 'is_well_formed<R, T, <template-parameter-1-3> >::is_well_formed'
main.cpp:54:20: note:   expected a class template, got 'reference_t'

或者,以下内容与 static_assert 相同:

// (#2)
template<class T>
using reference_t = void_t<T&>;

还有下面的作品,让我很纳闷:

// (#3)
template<class T>
using pointer_t = T*;

static_assert(is_well_formed<pointer_t, void>::value, "No pointer to void!?");

三个别名有什么区别?void_t<T&>是不是最优雅的方案?或者是否可以修改 is_well_formed 帮助程序结构以支持第一个 reference 版本?

编辑: 我用 MSVC"15" Preview 4 测试了代码,(#1)(#3) 工作包括断言。但是当我尝试 (#2) 时,void 引用的断言不起作用,即信息在替换过程中丢失并且永远不会选择 false_type 重载。哪个编译器是正确的?

is_well_formed 助手对应于 can_apply 结构,该结构曾在 SFINAE 上的 Stack Overflow 文档页面上记录过,我只是删除了参数包。完整示例代码:

#include <utility>

// Only defined in std for C++17
template <class...>
using void_t = void;

// (#1) Compiler error during substitution in is_well_formed
template<class T>
using reference_t = T&;

// (#2) Ok, asserts work
/*
template<class T>
using reference_t = void_t<T&>;
*/

// (#3) Ok, asserts work
template<class T>
using pointer_t = T*;

template<template<typename> typename R, typename T, typename = void>
struct is_well_formed 
    : std::false_type {};

template<template<typename> typename R, typename T>
struct is_well_formed<R, T, void_t<R<T>>> 
    : std::true_type {};

int main(int, char**)
{
    static_assert(is_well_formed<reference_t, double>::value, "No reference to double!?");
    static_assert(!is_well_formed<reference_t, void>::value, "Reference to void!?");

    static_assert(is_well_formed<pointer_t, double>::value, "No pointer to double!?");
    static_assert(is_well_formed<pointer_t, void>::value, "No pointer to void!?");

    return 0;
}

可能是编译器错误,用户 reported it on the GCC Bugzilla here after seeing this post. User 建议

template<class T>
using reference = decltype(std::declval<T&>());

作为 MSVC15 和 GCC 6.1.0 的工作替代方案。此外,他指出 MSVC 仍然需要很长的 void_t 定义

template <class...>
struct make_void { using type = void; };

template <typename... T>
using void_t = typename make_void<T...>::type;

而不是显而易见的

template <class...>
using void_t = void;

这阻止了原始 post 中的选项 (#2) 工作。