Rozważ następujący kod .
struct any
{
template <typename T>
operator T &&() const;
template <typename T>
operator T &() const;
};
int main()
{
int a = any{};
}
Tutaj drugi operator konwersji jest wybierany przez rozdzielczość przeciążenia. Dlaczego?
O ile rozumiem, dwa operatory są wydedukowane odpowiednio operator int &&() const
i operator int &() const
. Oba są w zestawie realnych funkcji. Czytanie przez [over.match.best] nie pomogło mi zrozumieć, dlaczego to drugie jest lepsze.
Dlaczego ta ostatnia funkcjonuje lepiej niż pierwsza?
template <typename T> operator T &&() const &&; template <typename T> operator T &() const &;
zadzwonić do pierwszego.Odpowiedzi:
Operator konwersji, który zwraca,
T&
jest preferowany, ponieważ jest bardziej wyspecjalizowany niż operator konwersji, który zwracaT&&
.Patrz C ++ 17 [temp.deduct.partial] / (3.2):
i / 9:
źródło
Operatory konwersji wydedukowanej wartości zwrotu są nieco dziwne. Ale podstawową ideą jest to, że działa jak parametr funkcji, aby wybrać, który z nich zostanie użyty.
I przy podejmowaniu decyzji pomiędzy
T&&
iT&
gdyT&
wygrywa w zasadach rozwiązywania przeciążenie. Ma to na celu:pracować.
T&&
może się równać z wartością, ale gdy dostępne są przeciążenia zarówno dla wartości, jak i dla wartości uniwersalnych, preferowana jest wartość dla tej wartości.Właściwy zestaw operatorów konwersji to prawdopodobnie:
lub nawet
aby uniknąć gryzienia nieudanego przedłużenia życia.
[FANTASTYCZNA OKAZJA]
Który następnie kończy się w zależności od „bardziej wyspecjalizowanych” reguł przy wybieraniu przeciążeń:
Zatem
operator T&&
nie jest on przynajmniej tak wyspecjalizowany jakoperator T&
, tymczasem żadne stany regułoperator T&
nie są tak wyspecjalizowane jakoperator T&&
, więcoperator T&
są bardziej wyspecjalizowane niżoperator T&&
.Bardziej wyspecjalizowane szablony wygrywają rozdzielczość przeciążenia mniejszą, a wszystko inne jest równe.
źródło
&&
vs&
podczas wybierania przeciążeń szablonu, ale wyłapanie dokładnego tekstu zajęłoby trochę czasu .Próbujemy zainicjować
int
anany
. Proces w tym celu:Dowiedz się, jak możemy to zrobić. To znaczy, określ wszystkich naszych kandydatów. Pochodzą one z nieprecyzyjnych funkcji konwersji, na które można przekonwertować
int
za pomocą standardowej sekwencji konwersji ( [over.match.conv] ). Sekcja zawiera tę frazę:Wybierz najlepszego kandydata.
Po kroku 1 mamy dwóch kandydatów.
operator int&() const
ioperator int&&() const
oba uważa się za przynosząceint
do celów wyboru funkcji kandydujących. Jaki jest najlepszy plon kandydataint
?Mamy remis, który woli odniesienia do wartości niż do wartości ( [over.ics.rank] /3.2.3 ). Jednak tak naprawdę nie wiążemy tutaj odniesienia, a przykład jest nieco odwrócony - dotyczy to przypadku, gdy parametr jest odniesieniem lvalue vs. rvalue.
Jeśli to nie ma zastosowania, przechodzimy do rozstrzygającego remisu [over.match.best] /2.5, który preferuje bardziej wyspecjalizowany szablon funkcji.
Ogólnie rzecz biorąc, ogólna zasada jest taka, że bardziej konkretna konwersja jest najlepszym dopasowaniem. Funkcja konwersji referencji wartości jest bardziej szczegółowa niż funkcja konwersji referencji przekazywania, więc jest preferowana. Nie ma nic w
int
inicjowaniu, co wymagałoby wartości (gdybyśmy zamiast tego inicjalizowaliint&&
, tooperator T&() const
nie byłby kandydatem).źródło
T&&
wygrane, prawda? Ach, ale nie mamy do czynienia z wartością. A może my? Przy wyborze naszych kandydatów niektóre słowa musiały określać, jak się dostaliśmyint&
iint&&
czy było to wiążące?