Perfect Forwarding
Content |
Wenn wir nicht mit lvalue references arbeiten und Parameter möglichst mit minimalen Aufwand an andere Funktionen weitergeben möchten, gibt es zwei Fälle:
-
Uns werden lvalues übergeben: Dann ist es am geschicktesten, diese per Referenz zu akzeptieren, um eine Kopie zu vermeiden.
-
Uns werden rvalues übergeben: Dann wäre es am besten, diese jeweils verschiebenderweise zu übermitteln, also mit std::move.
Das würde uns nötigen, jeweils zwei Varianten zu schreiben:
template<typename T> void do_sth(T& t) { f(t); // Weitergabe als Referenz } template<typename T> void do_sth(T&& t) { f(std::move(t)); // Verschieben des Parameters }
In C++ lassen sich glücklicherweise beide Varianten vereinigen, da bei einer Template-Funktion und && beide Varianten akzeptiert werden. Dann müssten wir aber in Abhängigkeit der Übergabe mal std::move verwenden, mal nicht. Dieses Problem erledigt std::forward:
template<typename T> void do_sth(T&& t) { f(std::forward<T>(t)); // Verschieben des Parameters }
Die explizite Angabe des Typs T bei std::forward ist wichtig, da aus T abgeleitet werden kann, ob es eine Parameterübergabe per lvalue oder rvalue war. Dann kann entsprechend bedingt std::move zum Einsatz kommen oder nicht.
Aufgabe
Fügen Sie noch eine Template-Funktion print_csv_line hinzu, die analog eine CSV-Zeile ausgibt. Dabei ist perfect forwarding einzusetzen.
Vorlage
#ifndef UPDATE_HPP #define UPDATE_HPP #include <cstdlib> #include <utility> namespace update_with_index_impl { template<typename F, typename Variable> void update(F&& f, std::size_t size, std::size_t index, Variable& var) { f(size, index, var); } template<typename F, typename Variable, typename... Variables> void update(F&& f, std::size_t size, std::size_t index, Variable& var, Variables&... vars) { f(size, index, var); update(std::move(f), size, index+1, vars...); } } // namespace update_with_index_impl template<typename F, typename... Variables> void update_with_index(F&& f, Variables&... vars) { update_with_index_impl::update(std::move(f), sizeof...(vars), 0, vars...); } #endif
#ifndef CSV_READER_HPP #define CSV_READER_HPP #include <iostream> #include <sstream> #include "update-with-index.hpp" template<typename... Variables> bool scan_csv_line(std::istream& in, Variables&... vars) { update_with_index([&in](std::size_t size, std::size_t index, auto& var) { char delimiter; if (index + 1 < size) { delimiter = ','; } else { delimiter = '\n'; } std::string s; if (std::getline(in, s, delimiter)) { std::istringstream is(s); is >> var; } }, vars...); return !!in; } #endif
#include <iostream> #include <string> #include "csv-reader.hpp" #include "csv-printer.hpp" int main() { std::string town; unsigned int population; unsigned int elevation; double area; while (scan_csv_line(std::cin, town, population, elevation, area) && print_csv_line(std::cout, town, population, elevation, area)) ; }