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:

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))
      ;
}