Skip to content

Latest commit

 

History

History
96 lines (74 loc) · 2.68 KB

File metadata and controls

96 lines (74 loc) · 2.68 KB

Zaśmiecanie interfejsów

Gdy chcemy napisać kod, który jest optymalny dla każdego przypadku, trzeba napisać kilka przeciążeń do każdej funkcji.

class Gadget;
void f(const Gadget&)     { std::cout << "const Gadget&\n"; }
void f(Gadget&)           { std::cout << "Gadget&\n"; }
void f(Gadget&&)          { std::cout << "Gadget&&\n"; }
void use(const Gadget& g) { f(g); }            // calls f(const Gadget&)
void use(Gadget& g)       { f(g); }            // calls f(Gadget&)
void use(Gadget&& g)      { f(std::move(g)); } // calls f(Gadget&&)

int main() {
    const Gadget cg;
    Gadget g;
    use(cg);       // calls use(const Gadget&) then calls f(const Gadget&)
    use(g);        // calls use(Gadget&) then calls f(Gadget&)
    use(Gadget()); // calls use(Gadget&&) then calls f(Gadget&&)
}

Czy można tego uniknąć?


Zadanie

Ulepsz funkcję use(), aby mogła przechwycić różne rodzaje referencji i aby nie tworzyć aż tylu przeciążeń.


Solution: Perfect Forwarding

T&& + std::forward() to rozwiązanie na zbytni rozrost interfejsów.

class Gadget;

void f(const Gadget&) { std::cout << "const Gadget&\n"; }
void f(Gadget&)       { std::cout << "Gadget&\n"; }
void f(Gadget&&)      { std::cout << "Gadget&&\n"; }

template <typename Gadget>
void use(Gadget&& g) {
    f(std::forward<Gadget>(g)); // forwards original type to f()
}

int main() {
    const Gadget cg;
    Gadget g;
    use(cg);       // calls use(const Gadget&) then calls f(const Gadget&)
    use(g);        // calls use(Gadget&) then calls f(Gadget&)
    use(Gadget()); // calls use(Gadget&&) then calls f(Gadget&&)
}

std::forward

Referencja przekazująca (forwarding reference), nawet do r-value, jest traktowana jako l-value wewnątrz funkcji szablonowych.

template <typename T>
void use(T&& t) {
    f(t);                   // t treated as l-value unconditionally
}
template <typename T>
void use(T&& t) {
    f(std::move(t));        // t treated as r-value unconditionally
}
template <typename T>
void use(T&& t) {           // forwards t as r-value if r-value was passed,
    f(std::forward<T>(t));  // forwards as l-value otherwise
}

Innymi słowy, std::forward() przywraca oryginalny typ referencji.