Шаблон псевдонимов для ссылочного типа, переданный в качестве аргумента шаблона шаблона в контексте SFINAE

У меня возникла следующая проблема с g++ 6.1.0 (-std=c++14 switch), и я не понимаю, почему компилятор отклоняет код.

У меня есть вспомогательная структура 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?

EDIT: Я тестировал код с помощью MSVC "15" Preview 4 и (#1) и (#3), включая утверждения. Но когда я пытаюсь (#2), утверждение для ссылки на пустоту не работает, то есть информация теряется во время замещения, а перегрузка false_type никогда не выбирается. Какой компилятор прав?

Помощник is_well_formed соответствует структуре can_apply, которая была однажды зарегистрирована на странице документации по переполнению стека SFINAE, я просто удалили пакеты параметров. Полный пример кода:

#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

Может быть ошибка компилятора и пользователь TC сообщили об этом в GCC Bugzilla здесь после просмотра этого сообщения. Пользователь Jarod42 предложил

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

в качестве рабочей альтернативы для MSVC15 и GCC 6.1.0. Кроме того, он отметил, что MSVC по-прежнему требует определения long 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) в исходном сообщении работать.