Historia pytania:
Szablony zostały dodane do C++ w celu efektywnej realizacji uniwersalnych algorytmów i struktur danych. Od samego początku wymagany był mechanizm automatycznego wnioskowania typów na podstawie argumentów wejściowych, aby ułatwić programistom korzystanie z szablonów.
Problem:
Mechanizm dedukcji nie zawsze jest oczywisty: występuje niejednoznaczność związana z odniesieniami, maskowaniem typów, częściowymi specjalizacjami, parametrami szablonów odnoszącymi się i stałymi. Czasami kompilator nie może wnioskować typu lub wnioskuje niespodziewany wynik.
Rozwiązanie:
Kompilator analizuje argumenty, porównuje je z parametrami szablonów, uwzględniając zasady kwalifikatorów cv, odniesienia i wskaźniki. Niedogodności i ograniczenia wymagają jawnego wskazania typu, gdy automatyczna dedukcja jest niemożliwa.
Przykład kodu:
template<typename T> void func(T arg) { /* ... */ } func(10); // T wnioskowane jako int func("abc"); // T wnioskowane jako const char* // Różnica między T arg a T& arg: template<typename T> void printRef(T& arg); // Nie przyjmie tymczasowego obiektu!
Kluczowe cechy:
Co się stanie, jeśli funkcja szablonowa przyjmuje T& i spróbujesz przekazać jej tymczasowy obiekt?
Kompilator nie będzie mógł wnioskować typu, ponieważ tymczasowy obiekt nie może być przekazany przez niekonstancyjną referencję. Pojawi się błąd kompilacji.
template<typename T> void foo(T& arg); foo(42); // Błąd!
Jak działa dedukcja typów z tablicami?
Tablice "rozpadają się" na wskaźniki przy przekazywaniu przez wartość, ale jeśli szablon przyjmuje referencję, rozmiar tablicy zostaje zachowany, co często wykorzystuje się do realizacji szablonów bezpiecznych pod względem rozmiaru.
template<typename T, size_t N> void arraySize(T (&arr)[N]) { std::cout << N; } int x[10]; arraySize(x); // Wyświetli 10
Dlaczego nie zawsze poprawnie jest używać auto do przechowywania wyników wywołania funkcji szablonowych?
Auto podczas dedukcji typu może "użyć" kwalifikatora const lub ref, co czasami prowadzi do niespodziewanych błędów kopiowania lub mutowalności.
auto x = funcReturningRef(); // x będzie przez wartość, a nie referencję!
Funkcja szablonowa uniwersalnego sortowania przyjmuje T&, użytkownik próbuje przekazać tymczasowy obiekt. W rezultacie kod nie kompiluje się, a błąd wprawia programistę w osłupienie.
Zalety:
Wady:
Sorter realizowany jest przez uniwersalne referencje (T&&) z użyciem std::forward, co pozwala zgrabnie działać zarówno z lvalue, jak i rvalue, co zwiększa wydajność i elastyczność.
Zalety:
Wady: