2016-09-12 24 views
10

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; 
} 
+1

Interesujące. Wydaje się, że to się nie uda, jeśli "reference_t" daje typ referencyjny. –

+1

Zgłoszono jako https://gc.gnu.org/bugzilla/show_bug.cgi?id=77575 –

+0

@ 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

Odpowiedz

0

Może to być błąd kompilatora i użytkownik T.C. zgłosił go na GCC Bugzilla here po obejrzeniu tego posta. Użytkownik Jarod42 zaproponował

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

jako alternatywa dla pracy zarówno MSVC15 i GCC 6.1.0. Ponadto zauważył, że MSVC wymaga jeszcze długiej void_t definicję

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

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

zamiast oczywistej

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

co uniemożliwiło opcję (#2) w oryginalnym poście z pracy.