Pytanie Zachowanie funkcji szablonu C ++


Powiedzmy, że mam tę funkcję:

bool f(int&& one, int&& two) { }

Jeśli spróbuję wywołać to za pomocą tego kodu:

int x = 4;
f(x, 5);

kompilator będzie narzekał, że nie jest w stanie przekonwertować x z lwartej referencji na referencję rvalue, co jest poprawne.

Teraz, jeśli skonwertuję f na funkcję szablonu podobną do tej:

template <class T, class U>
bool f(T&& one, U&& two) { }

wtedy mogę nazwać to odwołaniem do lwartości:

int x = 5;
f(x, 5);

Dlaczego tak jest? Dlaczego kompilator nie skarży się w tym przypadku?


18
2017-10-03 07:24


pochodzenie




Odpowiedzi:


Ponieważ istnieje odliczanie argumentów w szablonie, następuje zwijanie referencji. To właśnie nazywa Scott Meyers uniwersalne odniesienia. The U&& faktycznie się int &. Jest fajnie artykuł i wideo o tym, jak to działa i jak można z niego korzystać.


8
2017-10-03 07:31





Według § 8.3.3 / 6. Jest to referencyjna reguła zwijania.

template <class T> void func(T&&)  // Accepts rvalue or lvalue
void func(T&&)                     // Accepts rvalue only
void func(T&)                      // Accepts lvalue only

Warto przykład z wersji standardowej:

int i;
typedef int& LRI;
typedef int&& RRI;

LRI& r1 = i;           // r1 has the type int&
const LRI& r2 = i;     // r2 has the type int&
const LRI&& r3 = i;    // r3 has the type int&

RRI& r4 = i;           // r4 has the type int&
RRI&& r5 = 5;          // r5 has the type int&&

decltype(r2)& r6 = i;  // r6 has the type int&
decltype(r2)&& r7 = i; // r7 has the type int&

10
2017-10-03 07:28



Jest to trochę sprzeczne z intuicją ... - Violet Giraffe
@VioletGiraffe witamy w świecie C ++ - jk.
@jk .: I tam myślałem, że rozumiem większość tego świata! - Violet Giraffe
Nie tylko jest to zwijanie odwołań, ale także dziwactwo specyfikacji dedukcji typów, zobacz [temp.deduct.call] / 3. (Myślę też, że odnosisz się do 8.3.2 / 6, a nie 8.3.3 / 6) - dyp


Dzieje się tak z powodu dodania reguł zwijania odwołań w C ++ 11

A& & becomes A&
A& && becomes A&
A&& & becomes A&
A&& && becomes A&&

W szablonach reguły te są stosowane, ale nie w normalnej funkcji, nie ma normalnego zapadania się odniesienia w funkcji. Istnieją inne szczególne sytuacje, w których zapadanie referencyjne wystąpi jak w obecności auto, decltype lub typedef (to obejmuje using deklaracje) To wyjaśnia wyniki twojej kompilacji. Zawinięcie referencyjne musiało zostać dodane w c ++ 11, ponieważ w przeciwnym razie użycie odnośników, takich jak A & &, stałoby się błędami, ponieważ nie można mieć odniesienia do odniesienia.


6
2017-10-03 07:28



Podczas gdy w przypadku "uniwersalnego odniesienia" występuje zwijanie odniesienia, prawdziwa magia dzieje się przed tym krokiem i występuje podczas dedukcji argumentu szablonu. Twoja notka o zwijaniu referencji nie działa w normalnej funkcji jest po prostu błędna; Zawijanie referencji może się zdarzyć w dowolnym miejscu: using T = int&&; void foo(T& x); tutaj x jest a int& ze względu na zwijanie referencji. - Simple
@ Prosty wygląda jak twoje prawo, już wiedziałem, że deklinaty i auto mogą mieć zwijanie odniesienia, nie wiedziałem, że może to spowodować typedef, zmienię moją odpowiedź - aaronman