Wystąpił następujący problem z przełącznikiem G ++ 6.1.0 (-std=c++14
) i nie rozumiem, dlaczego kompilator odrzuca kod.Szablon aliasu dla typu odniesienia przekazanego jako argument szablonu szablonu w kontekście SFINAE
Mam struct pomocnika is_well_formed
który sprawdza, czy dostarczony szablon szablon argumentem jest dobrze uformowane kiedy podstawiając inny rodzaj dostarczanego do niego:
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 {};
Chcę sprawdzić, czy typ jest referenceable. Więc mój pomysł był napisać następujące:
// (#1)
template<class T>
using reference_t = T&;
static_assert(!is_well_formed<reference_t, void>::value, "Reference to void!?");
Ale pojawia się błąd kompilatora:
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'
Alternatywnie następujących utworów z tej samej static_assert
:
// (#2)
template<class T>
using reference_t = void_t<T&>;
Ponadto następujące działa, co naprawdę mnie zastanawia:
// (#3)
template<class T>
using pointer_t = T*;
static_assert(is_well_formed<pointer_t, void>::value, "No pointer to void!?");
Jaka jest różnica między trzema aliasami? Czy rozwiązanie void_t<T&>
jest najbardziej eleganckie? Czy jest możliwa modyfikacja struktury pomocnika is_well_formed
w celu obsługi pierwszej wersji reference
?
EDIT: testowałem kodu z MSVC "15" Preview 4 i (#1)
i (#3)
pracy w tym twierdzi. Ale kiedy próbuję (#2)
, assert dla odniesienia void nie działa, tj. Informacje zostają utracone podczas podstawiania, a przeciążenie false_type
nigdy nie jest wybierane. Który kompilator ma rację?
Pomocnik is_well_formed
odpowiada strukturze can_apply
z tego, co zostało kiedyś udokumentowane na stronie dokumentacji przepełnienia stosu na SFINAE, właśnie usunąłem pakiety parametrów. Pełny przykład kodu:
#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;
}
Interesujące. Wydaje się, że to się nie uda, jeśli "reference_t" daje typ referencyjny. –
Zgłoszono jako https://gc.gnu.org/bugzilla/show_bug.cgi?id=77575 –
@ T.C. Cóż, to było szybkie.: D Naprawdę dziwne, jeśli to rzeczywiście błąd i nikt go wcześniej nie napotkał/zgłosił - to nie wygląda na najbardziej egzotyczny kod ... – w1th0utnam3