Dlaczego wybrano to przeciążenie operatora konwersji?

27

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 &&() consti 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?

avakar
źródło
Jestem prostym kotem i powiedziałbym, że to musi być drugi raz, gdy wprowadzenie drugiego byłoby przełomową zmianą w C ++ 11. Gdzieś będzie w standardzie. Dobre pytanie, proszę wyrazić opinię. (Uwaga eksperci: jeśli ten komentarz jest miazgą, proszę powiedz mi!)
Batszeba
3
FWIW, każe template <typename T> operator T &&() const &&; template <typename T> operator T &() const &;zadzwonić do pierwszego.
NathanOliver,
5
@LightnessRaceswithMonica Operatory konwersji pozwalają na to, w przeciwnym razie nie byłoby możliwości posiadania wielu operatorów konwersji.
NathanOliver,
1
@Bathsheba Nie sądzę, że to jest powód tego. To tak, jakby powiedzieć, że konstruktorów ruchów nigdy nie można wybrać na podstawie rozdzielczości przeciążenia, ponieważ byłaby to przełomowa zmiana. Jeśli piszesz konstruktor ruchu, decydujesz się na „złamanie” kodu przez tę definicję.
Brian,
1
@LightnessRaceswithMonica Sposób, w jaki trzymam go prosto w głowie, traktuję typ zwracany jako nazwę funkcji. Różne typy to różne nazwy to sukces :)
NathanOliver

Odpowiedzi:

13

Operator konwersji, który zwraca, T&jest preferowany, ponieważ jest bardziej wyspecjalizowany niż operator konwersji, który zwraca T&&.

Patrz C ++ 17 [temp.deduct.partial] / (3.2):

W kontekście wywołania funkcji konwersji używane są typy zwracanych szablonów funkcji konwersji.

i / 9:

Jeśli dla danego typu, odliczenie powiedzie się w obu kierunkach (czyli typy są identyczne po przemianach powyżej) i zarówno Pi Abyły typy referencyjne (przed zastąpione typu, o którym mowa powyżej): - jeśli typ z szablonu argumentu było odwołaniem do wartości, a typ z szablonu parametru nie był, typ parametru nie jest uważany za co najmniej tak wyspecjalizowany jak typ argumentu; ...

Brian
źródło
12

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&&i T&gdy T&wygrywa w zasadach rozwiązywania przeciążenie. Ma to na celu:

template<class T>
void f( T&& ) { std::cout << "rvalue"; }
template<class T>
void f( T& ) { std::cout << "lvalue"; }

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:

template <typename T>
operator T&&() &&;

template <typename T>
operator T &() const; // maybe &

lub nawet

template <typename T>
operator T() &&;

template <typename T>
operator T &() const; // maybe &

aby uniknąć gryzienia nieudanego przedłużenia życia.

3 Typy używane do określania kolejności zależą od kontekstu, w którym odbywa się częściowe uporządkowanie:

[FANTASTYCZNA OKAZJA]

(3.2) W kontekście wywołania funkcji konwersji stosowane są typy zwracanych szablonów funkcji konwersji.

Który następnie kończy się w zależności od „bardziej wyspecjalizowanych” reguł przy wybieraniu przeciążeń:

(9.1) jeżeli typ z szablonu argumentu był odwołaniem do wartości, a typ z szablonu parametru nie, typ parametru nie jest uważany za co najmniej tak wyspecjalizowany jak typ argumentu; Inaczej,

Zatem operator T&&nie jest on przynajmniej tak wyspecjalizowany jak operator T&, tymczasem żadne stany reguł operator T&nie są tak wyspecjalizowane jak operator T&&, więc operator T&są bardziej wyspecjalizowane niż operator T&&.

Bardziej wyspecjalizowane szablony wygrywają rozdzielczość przeciążenia mniejszą, a wszystko inne jest równe.

Jak - Adam Nevraumont
źródło
Ktoś dodał tag prawnika języka, kiedy odpowiadałem; Mógłbym po prostu usunąć. Mam niejasną pamięć o czytaniu tekstu, który stwierdza, że ​​jest on traktowany jako parametr służący do wybrania, który z nich jest wywoływany, a tekst mówiący o tym, jak traktowane jest &&vs &podczas wybierania przeciążeń szablonu, ale wyłapanie dokładnego tekstu zajęłoby trochę czasu .
Yakk - Adam Nevraumont,
4

Próbujemy zainicjować intan any. Proces w tym celu:

  1. 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ć intza pomocą standardowej sekwencji konwersji ( [over.match.conv] ). Sekcja zawiera tę frazę:

    Wywołanie funkcji konwersji zwracającej „odniesienie X” jest wartością typu X, a zatem uważa się, że taka funkcja konwersji przynosi Xten proces wyboru funkcji kandydujących.

  2. Wybierz najlepszego kandydata.

Po kroku 1 mamy dwóch kandydatów. operator int&() consti operator int&&() constoba uważa się za przynoszące intdo celów wyboru funkcji kandydujących. Jaki jest najlepszy plon kandydata int?

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 intinicjowaniu, co wymagałoby wartości (gdybyśmy zamiast tego inicjalizowali int&&, to operator T&() constnie byłby kandydatem).

Barry
źródło
3.2.3 faktycznie mówi, że 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śmy int&i int&&czy było to wiążące?
Jak - Adam Nevraumont,
@ Yakk-AdamNevraumont Nie, że woli się odniesienia do wartości od odniesień do wartości. Myślę, że i tak jest to prawdopodobnie niewłaściwa sekcja, a „bardziej wyspecjalizowany” remis jest właściwy.
Barry