2017-06-18 17 views
5

próbuję tworzyć częściowo specjalistycznego szablonu, i specjalizują się nawet dalej, jeśli uchwalił std::unique_ptrC szablon ++ częściowa specjalizacja - Najczęściej wyspecjalizowany z unique_ptr <t>

template <typename T, typename = void> 
struct Foo; 

// A 
template <typename T> 
struct Foo<std::unique_ptr<T>, typename std::enable_if<std::is_class<T>::value>::type> {...}; 

// B 
template <typename T> 
struct Foo<T, typename std::enable_if<std::is_class<T>::value>::type> {...}; 

void fn() { 
    Foo<std::unique_ptr<T>> foo; 
} 

mojego zrozumienia jest to, że jest bardziej wyspecjalizowane niż B, a powinien być tym, który był użyty. Ale przynajmniej w MSVC 2015 pojawia się błąd:

error C2752: 'Foo<FieldT,void>': more than one partial specialization matches the template argument list 

Czy jest tu coś, czego mi brakuje?

+0

Cóż, dwie instrukcje 'std :: enable_if' powinny być komplementarne, prawda? –

+0

Wygląda na to, że może to rozwiązać, ale moje zrozumienie "bardziej wyspecjalizowanych" zasad powinno uczynić to niepotrzebnym. – Arelius

Odpowiedz

3

Pytanie: która specjalizacja powinna być używana pod numerem foo<std::unique_ptr<int>>?

Należy zauważyć, że int nie jest klasą, więc wartość std::is_class<T>::value jest fałszywa, a wersja A nie pasuje.

Jeśli chcesz, że wersja A jest zawsze stosowany, gdy pierwszy parametr szablonu jest std::unique_ptr, można napisać wersję A jak

template <typename T> 
struct foo<std::unique_ptr<T>, 
      typename std::enable_if<std::is_class< 
       std::unique_ptr<T>>::value>::type> 
{ static int const value = 1; }; 

lub można napisać foo następująco

template <typename T, 
      typename = typename std::enable_if<std::is_class<T>::value>::type> 
struct foo; 

template <typename T> 
struct foo<std::unique_ptr<T>> 
{ static int const value = 1; }; 

template <typename T> 
struct foo<T> 
{ static int const value = 2; }; 

Więc A stanie się bardziej wyspecjalizowaną wersją niż B i Twój kod może się skompilować. W przeciwnym razie wersja A jest w rzeczywistości bardziej wyspecjalizowaną wersją niż ta, którą kompilator jej nie rozpoznaje.

Zauważmy, że z

template <typename T> 
struct foo<std::unique_ptr<T>, 
      typename std::enable_if<std::is_class< 
       std::unique_ptr<T>>::value>::type> 
{ static int const value = 1; }; 

kompilacji kodu i że std::is_class<std::unique_prt<T>>::value jest kiedykolwiek true; ale jeśli piszesz

template <typename T> 
struct foo<std::unique_ptr<T>, 
      typename std::enable_if<true>::type> 
{ static int const value = 1; }; 

(który w rzeczywistości jest równoważna) kod nie kompiluje

- Edit -

Jeśli chcesz takim przypadku foo<std::unique_ptr<int> mecz B, (na przykładzie) można utworzyć cechy typu następująco

struct foo<T> 
{ static int const value = 2; }; 

template <typename T> 
struct proValue 
    : std::integral_constant<int, 2> 
{ }; 

template <typename T> 
struct proValue<std::unique_ptr<T>> 
    : std::integral_constant<int, std::is_class<T>::value ? 1 : 2> 
{ }; 

i zdefiniować jako foo Wynika

template <typename T, int = proValue<T>::value> 
struct foo; 

template <typename T> 
struct foo<std::unique_ptr<T>, 1> 
{ static int const value = 1; }; 

template <typename T> 
struct foo<T, 2> 
{ static int const value = 2; }; 

Poniżej znajduje się pełny compilable przykład

#include <memory> 
#include <vector> 
#include <iostream> 
#include <type_traits> 

class Test 
{ }; 

template <typename T, 
      typename = typename std::enable_if<std::is_class<T>::value>::type> 
struct foo; 

template <typename T> 
struct foo<std::unique_ptr<T>> 
{ static int const value = 1; }; 

template <typename T> 
struct foo<T> 
{ static int const value = 2; }; 

template <typename T> 
struct proValue 
    : std::integral_constant<int, 2> 
{ }; 

template <typename T> 
struct proValue<std::unique_ptr<T>> 
    : std::integral_constant<int, std::is_class<T>::value ? 1 : 2> 
{ }; 

template <typename T, int = proValue<T>::value> 
struct bar; 

template <typename T> 
struct bar<std::unique_ptr<T>, 1> 
{ static int const value = 1; }; 

template <typename T> 
struct bar<T, 2> 
{ static int const value = 2; }; 

int main() 
{ 
    std::cout << foo<std::vector<int>>::value << '\n';  // print 2 
    std::cout << foo<std::unique_ptr<int>>::value << '\n'; // print 1 
    std::cout << foo<std::unique_ptr<Test>>::value << '\n'; // print 1 

    std::cout << bar<std::vector<int>>::value << '\n';  // print 2 
    std::cout << bar<std::unique_ptr<int>>::value << '\n'; // print 2 
    std::cout << bar<std::unique_ptr<Test>>::value << '\n'; // print 1 
} 
+0

Ahh Rozumiem. Zasadniczo kompilator próbuje zapewnić, że jedna wersja jest bardziej wyspecjalizowana na wszystkich argumentach niż inne, a ponieważ typ 'std :: enable_if :: value> :: type' jest inny niż' std :: enable_if > Porównanie wartości> value> :: type' nie powiedzie się! – Arelius

2

Ustawiłem mały przykład, aby odtworzyć błąd nieco ładniej.

#include <iostream> 
#include <memory> 
#include <type_traits> 

template < typename T, typename = void > 
struct foo; 

template < typename T > 
struct foo < std::unique_ptr<T>, 
      typename std::enable_if<std::is_class<T>::value>::type > 
{ 
    static int const value = 1; 
}; 

template < typename T > 
struct foo < T, 
      typename std::enable_if<std::is_class<T>::value>::type > 
{ 
    static int const value = 2; 
}; 

class Test; 

int main() 
{ 
    std::cout << foo< std::unique_ptr<Test> >::value << '\n'; 
} 

myślę błędu z Clang dość jednoznaczną

test.cpp:26:16: error: ambiguous partial specializations of 
     'foo<std::unique_ptr<Test, std::default_delete<Test> >, void>' 
    std::cout << foo< std::unique_ptr<Test> >::value << '\n'; 
      ^
test.cpp:9:8: note: partial specialization matches [with T = Test] 
struct foo < std::unique_ptr<T>, 
    ^
test.cpp:16:8: note: partial specialization matches [with T = std::unique_ptr<Test, 
     std::default_delete<Test> >] 
struct foo < T, 
    ^
1 error generated. 

Jak kompilator wie, że chcesz się wyprowadzić T = Test z pierwszej specjalizacji zamiast T = std::unique_ptr<Test>? Ponadto w obu przypadkach T jest klasą, która sprawia, że ​​std::enable_if jest bez znaczenia.

+0

Myślę, że pytanie brzmiało: dlaczego jest niejednoznaczne? –

+0

@PasserBy odpowiedź jest w dwóch ostatnich zdaniach: jest niejednoznaczna, ponieważ nie ma nic, czego kompilator mógłby użyć do dwuznaczności tych dwóch przypadków: oba są jednakowo ważne. – rubenvb

+0

@rubenvb problem był * Rozumiem, że A jest bardziej wyspecjalizowany niż B, i powinien być tym, który został użyty *, na który to nie odpowiada. –

1

Oprócz tego, co Henri powiedział w swojej odpowiedzi, można (w ograniczonym sensie) to obejść pisząc niemal trywialne is_unique_ptr cecha.

Live demo here.

Pominąłem kod, ponieważ jest on zasadniczo wadliwy.


Można zobaczyć, jak to już nie dla nietrywialne std::unique_ptr, ale które mogą być rozwiązane przez rozszerzenie cechę is_unique_ptr. Zauważ, że implementacje są bardzo dozwolone, aby dodać dodatkowe (domyślne) parametry szablonów, jak chcą, więc nigdy nie będzie to wodoszczelność.

Bardziej właściwe rozwiązanie polega również zmodyfikowanie foo szablonu specjalizacji:

#include <iostream> 
#include <memory> 
#include <type_traits> 

template<typename T> 
struct is_unique_ptr : std::false_type {}; 

template<typename... UniquePtrArgs> 
struct is_unique_ptr<std::unique_ptr<UniquePtrArgs...>> : std::true_type {}; 

template<typename T, typename = void> 
struct foo; 

template<typename... UniquePtrArgs> 
struct foo<std::unique_ptr<UniquePtrArgs...>> 
{ 
    static int const value = 1; 
}; 

template<typename T> 
struct foo<T, typename std::enable_if<!is_unique_ptr<T>::value && std::is_class<T>::value>::type> 
{ 
    static int const value = 2; 
}; 

class Test; 

void f(Test*); 

int main() 
{ 
    std::cout << foo<std::unique_ptr<Test>>::value << '\n'; 
    std::cout << foo<Test>::value << '\n'; 
    std::cout << foo<std::unique_ptr<Test, decltype(&f)>>::value << '\n'; 
} 

Live demo here.

Zamiast specjalizujący dla std::unique_ptr, to może być bardziej sensowne, aby specjalizować się w sprawie rodzajów z pewną własność std::unique_ptr jesteś zmobilizowania i specjalizacji, więc nie jesteś przywiązany do konkretnego typu, ale raczej specyficzną właściwość.

+0

Dzięki, w końcu rozwiązałem problem z tym, że moja sprawa unique_ptr również ma to w porównaniu, a następnie zmieniając implementację, aby nie przejmować się tym, że podstawowa T nie jest klasą. Ale mogło to być również dobre. – Arelius

Powiązane problemy