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

时间:2016-09-12 23:22:21

标签: c++ templates c++14 sfinae

我在使用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&#34; 15&#34;测试了代码预览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;
}

1 个答案:

答案 0 :(得分:0)

这可能是一个编译器错误,用户T.C.在看到这篇文章后在GCC Bugzilla here上报告了它。用户Jarod42提议

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;

阻止原始帖子中的选项(#2)生效。