2017-06-03 24 views
8

gcc 8.0.0 i clang 5.0.0 nie zgadzają się na zachowanie tego programu: zachowanieczęściowe uporządkowanie odniesienia spedycji i normalnego odniesienia z odliczenia prowadzi

#include <iostream> 

template <typename T> 
struct A { 
    A(const T&) { std::cout << __PRETTY_FUNCTION__ << '\n'; } 
    A(T&&)  { std::cout << __PRETTY_FUNCTION__ << '\n'; } 
}; 

template <typename U> A(U&&) -> A<double>; 

int main() { 
    int i = 0; 
    const int ci = 0; 

    A a1(0); // both say A<double> 
    A a2(i); // both say A<double> 
    A a3(ci); // gcc says A<int>, clang says A<double> 
} 

gcc nie ma sensu do mnie - jeśli przeciążenie const T& jest preferowany do U&& przeciążenie dla lwartość const int, dlaczego nie jest przeciążenie T&& preferowane do przeciążenia U&& dla rvalue int? clang ma dla mnie więcej sensu (żadna z funkcji nie jest bardziej wyspecjalizowana niż druga, więc wygrywa przewodnik dedukcji).

Kto ma rację?

+0

Pójdę na to. Odliczenie dotyczy rwartości (referencji). 'ci' jest lwartością' const', i istnieje konstruktor, który przyjmuje jako parametr parametr "const". –

+0

Przepraszam za brak wiedzy, ale jak się nazywa ta składnia '->'? – HolyBlackCat

Odpowiedz

7

Jesteśmy ponownie w częściowym zamówieniu ziemi. Rodzaj syntetyzowanych parametrów szablonu funkcji są

T&&  // #1: not a forwarding reference 
const T& // #2 
U&&  // #3: a forwarding reference 

pre-częściowy transformacji zamawiania strips away referenceness a potem na top-level cv-qualification, pozostawiając nas z gołymi typu we wszystkich trzech przypadkach. Wynika z tego, że we wszystkich trzech przypadkach odliczenie jest skuteczne w obu kierunkach. Jesteśmy teraz w lewo z [temp.deduct.partial]/9 „s tiebreaker:

Jeśli dla danego typu, odliczenie powiedzie się w obu kierunkach (czyli typy są identyczne po powyższych przemian) i zarówno P i były typami referencyjnymi (przed zastępowane typu określonego powyżej):

  • jeśli typ z szablonu argument lwartością odniesienia i rodzaju z szablonu parametr nie typ parametr nie jest uważany za przynajmniej tak wyspecjalizowani jak typ argumentu; w przeciwnym razie,
  • , jeśli typ z szablonu argumentu jest bardziej kwalifikowany cv niż typ z szablonu parametrów (jak opisana powyżej ), typ parametru nie jest uważany za co najmniej tak wyspecjalizowany jak typ argumentu.

Dla U&& vs T&&, ani zasada obowiązuje i nie ma zamawiania. W przypadku modelu U&& i const T& typ parametru U&& nie jest uważany za co najmniej tak wyspecjalizowany jak typ argumentu const T& na pierwszy punktor.

Częściowe zamówienie powoduje, że # 2 jest bardziej wyspecjalizowany niż # 3, ale stwierdza, że ​​# 1 i # 3 są nierozróżnialne. GCC jest poprawne.

To powiedziawszy, może to być niedopatrzenie w zasadach częściowego zamawiania. Odliczanie szablonów klas jest pierwszym razem, gdy mamy "referencję rwartości do nieuwzględnionego cv parametru szablonu, który nie jest odniesieniem do przekierowania". Poprzednio, w przypadkach podwójnego odwołań, odsyłacze do przekazywania dalej zawsze będą tracić do nieprzesyłowych referencji rvalue na drugim punkcie (ponieważ jedynym sposobem uzyskania nieprzeczytanych referencji rvalue jest cv T&& dla niektórych niepustych cv).

+0

To wszystko ma sens. Czy według ostatniego akapitu pierwszy punkt powinien obejmować także nieprzekazywanie dalej wartości rvalue (tj. Preferujące "T &&" do "U &&")? – Barry

+0

Iirc, rezolucja o przeciążeniu ma pociski typu tie breaker, które obejmują zarówno częściowe porządkowanie, jak i sprawdzenie, czy jeden z dwóch kandydatów był przewodnikiem dedukcji. Wygląda na to, że GCC i klang stosują te łamacze tieru w różnych zamówieniach. Nie sądzę, że chodzi o brak porozumienia w sprawie częściowego zamawiania, ale jestem teraz na androidzie, więc nie mogę tego zbadać. –

+0

@ JohannesSchaub-litb Masz na myśli Clang nie implementuje [P0620] (https://wg21.link/P0620)? Może. Ale pytanie "dlaczego nie # 1 bije # 3, gdy # 2 ma", to częściowe porządkowanie. –