2017-01-13 32 views
14

Rozważmy następujący kod:Przeciążone funkcja szablon ujednoznacznienie z `std :: enable_if` i non-wyprowadzona kontekście

template <typename T> 
struct dependent_type 
{ 
    using type = T; 
}; 

template <typename T> 
auto foo(T) -> std::enable_if_t<std::is_same<T, int>{}> 
{ 
    std::cout << "a\n"; 
} 

template<typename T> 
void foo(typename dependent_type<T>::type) 
{ 
    std::cout << "b\n"; 
} 
  • Pierwszy przeciążenie foo może wywnioskować T od jego wywołania.

  • Drugie przeciążenie foo to non-deduced context.

int main() 
{  
    foo<int>(1);  // prints "b" 
    foo<double>(1.0); // prints "b" 
    foo(1);   // prints "a" 
} 

Dlaczego foo<int>(1) print "B", a nie "A"?

wandbox example

+0

Myślę, że odpowiedź brzmi „b jest bardziej wyspecjalizowane, więc zawsze nazywa, jeśli to możliwe”, a powodem będzie leżeć gdzieś [tutaj] (http://en.cppreference.com/w/cpp/language/function_template) (zaczyna się w połowie strony). To trochę dla mnie jednak. – Quentin

+0

Dobre pytanie. Dziękuję Ci.+1 – skypjack

+0

Następnym razem polecam opublikowanie kodu jako pojedynczego fragmentu, aby ludzie mogli kopiować/wklejać jednorazowo, zamiast kopiować/wklejać dwa razy w celu wypróbowania kodu (i albo mieć odpowiedni zawierać dyrektywy lub demonstrować wybrane przeciążenie inaczej). –

Odpowiedz

12

Zasadniczo częściowe zasady zamawiania powiedzieć, że przeciążenie dependent_type jest bardziej wyspecjalizowany powodu, że brak wywnioskować kontekście.

Proces zamawiania funkcji szablonu polega na transformacji typów funkcji szablonu i wykonywaniu odliczeń szablonów po kolei, po przejściu z pierwszego szablonu (ten, który przyjmuje T) do drugiego (ten, który przyjmuje dependent_type), a następnie od drugiego do pierwszego.

Zasady są zbyt skomplikowane, aby można je było tutaj replikować, ale przeczytaj [temp.func.order] i fragmenty, do których prowadzą linki, jeśli chcesz poznać szczegóły. Oto krótki uproszczenie:

Dla każdego parametru szablonu funkcji szablonu, tworzą unikalny rodzaj i zastąpić parametr z tym. Transformowane typy na ten przykład to:

void foo(UniqueType); //ignoring the SFINAE for simplicity 
void foo(typename dependent_type<UniqueType>::type); 

Następnie wykonać szablon odliczenia w dwóch kierunkach: po użyciu parametrów pierwszego szablonu jako argumenty do drugiego, a raz przy użyciu parametrów drugiego jako argumenty do pierwszy. Jest to podobne do wykonywania odliczenia na tych wywołań funkcji:

//performed against template <class T> void foo(typename dependent_type<T>::type); 
foo(UniqueType{});      

//performed against template <class T> void foo(T);   
foo(dependent_type<UniqueType>::type{}); 

Realizując te potrącenia, staramy się rozpoznać, czy ktoś jest bardziej wyspecjalizowane przeciążenie potem drugą. Kiedy próbujemy pierwszego, dedukcja się nie udaje, ponieważ typename dependent_type<T>::type jest kontekstem niewydechem. Na drugim, odliczenie uda bo dependent_type<UniqueType>::type tylko UniqueType, więc T jest wyprowadzona do UniqueType.

Od odliczenie udało począwszy od drugiego szablonu do pierwszego, drugi szablon jest brane jako bardziej wyspecjalizowane niż pierwszy. Ostateczny wynik jest taki, że rozdzielczość przeładowania preferuje drugi szablon dla foo<int>(1).

+0

Czy mógłbyś bardziej szczegółowo opisać, co masz na myśli przez "transformowanie" * i co masz na myśli, używając terminów * "pierwszy szablon" * i * "drugi szablon" *? Rozumiem, że zasady są skomplikowane, ale jeśli muszę być szczera, twoja odpowiedź wcale nie poprawia mojego zrozumienia tej sytuacji. –

+3

@VittorioRomeo wyjaśniono to całkiem dobrze [tutaj] (http://stackoverflow.com/a/17008568/3953764). Kompilator może wydedukować 'T', gdy argument' dependent_typu :: type {} 'jest używany jako argument, ale nie może wydedukować' T' z 'nazwa_pliku zależnego :: type' po przekazaniu' UniqueType {} ', więc wniosek jest taki, że 'b' jest bardziej wyspecjalizowany –

+1

@VittorioRomeo Próbowałem wyjaśnić to nieco więcej, czy to pomaga? – TartanLlama