Parameterpack verändern
-
Hallo,
ich habe folgende Funktion:
template<typename ...Params> void execute( std::string const& name, Params&&... params ) { impl()->execute( name, std::forward<Params>( params )... ); }Ich möchte jetzt für std::string-Parameter den Wert verändern, gibt's da ne Möglichkeit?
-
-
mit eigenem Forwarder?
template<typename T> using reference_or_string_t = std::conditional_t<std::is_same_v<T&, std::string&>, std::string, T&&>; template<typename T> constexpr reference_or_string_t<T> my_forward(std::remove_reference_t<T>& t) { return std::forward<T>(t); } template<> std::string my_forward<std::string>(std::string& s) { return s + s; } template<> std::string my_forward<std::string&>(std::string& s) { return s + s; } template<typename ...Params> void execute( std::string const& name, Params&&... params ) { impl()->execute( name, my_forward<Params>( params )... ); }
-
@camper
Mal eine Frage, da ich das Problem selten habe.Was hälst du von der folgenden KI Antwort:
Du willst also innerhalb deiner variadischen Template‑Funktion bestimmte Parameter anders behandeln, sobald sie std::string sind. Das ist ein klassischer Fall für Template‑Überladung, SFINAE, oder if constexpr (C++17+).
Der Knackpunkt:
Params&&... nimmt alles entgegen, aber du kannst beim Weiterreichen unterscheiden, ob ein Parameter ein std::string ist und ihn vorher modifizieren.
Lösung: if constexpr + Parameter-Pack-Expansion
Wenn du C++17 hast, ist das die eleganteste Variante:template<typename ...Params> void execute(std::string const& name, Params&&... params) { impl()->execute( name, (modifyIfString(std::forward<Params>(params)))... ); } template<typename T> T&& modifyIfString(T&& value) { return std::forward<T>(value); } inline std::string modifyIfString(std::string value) { // Beispiel: String verändern value += "_modified"; return value; }Was passiert hier?
Für jeden Parameter wird modifyIfString(...) aufgerufen.Für Nicht‑Strings wird die generische Version gewählt → unverändert weitergereicht.
Für std::string wird die spezialisierte Overload gewählt → du kannst den Wert ändern.
Alternative: if constexpr direkt im Pack
Auch möglich:template<typename ...Params> void execute(std::string const& name, Params&&... params) { impl()->execute( name, ( [] (auto&& p) { using T = std::decay_t<decltype(p)>; if constexpr (std::is_same_v<T, std::string>) { return p + "_modified"; } else { return std::forward<decltype(p)>(p); } }(std::forward<Params>(params)) )... ); }🧩 Warum geht das nicht direkt in der Signatur?
Weil Params&&... perfekt weiterleitet und du nicht einfach einzelne Typen „abfangen“ kannst, ohne eine zusätzliche Schicht einzuziehen.
Du brauchst also entweder:eine Hilfsfunktion (modifyIfString), oder
ein Lambda mit if constexpr.
Wenn du Strings by reference ändern willst
Dann musst du aufpassen:
Variadische Templates + Forwarding können Referenzen verlieren, wenn du nicht sauber arbeitest.Beispiel für echte Mutation:
inline void modifyIfString(std::string& s) { s += "_changed"; } template<typename T> void modifyIfString(T&) {} template<typename ...Params> void execute(std::string const& name, Params&&... params) { (modifyIfString(params), ...); // Strings werden verändert impl()->execute(name, std::forward<Params>(params)...); }Wenn du mir sagst, wie genau du den String verändern willst (kopieren, mutieren, ersetzen, trimmen …), kann ich dir die optimale Variante dafür bauen.
-
Vielen Dank an euch beide, ich habe jetzt einen Mix aus campers und Quiche Lorraines Ansätzen. Das eigentlich Problem ist, dass ich ein db-Interface habe, das per Parameter-Pack Parameter entgegen nimmt. Wenn diese Parameter String-Parameter sind, dann möchte das db-Interface, dass die Strings in dem Encoding kodiert sind, wie es in den db-Verbindungsoptionen festgelegt wurde (üblicherweise UTF8). Um das nicht jedes Mal den Benutzer machen lassen zu müssen, habe ich eine Zwischenschicht eingeführt, die die Parameter Packs nach strings durchsucht und bei einem Treffer den string automatisch mit dem richtigen Encoding kodiert. Das sind leider nicht nur strings (
std::string&), sondern auchchar*,char[]in alle möglichenconstundvolatileVariationen. Meine gekürzte Lösung sieht jetzt so aus, den Boilerplate für die type_traits habe ich mal ausgelassen:template<typename T> bool constexpr replace_ref_v = is_string_ref_v<T> || is_char_array_ref_v<T> || is_char_pointer_ref_v<T>; template<typename T> using replacement_forward_type_t = std::conditional_t<replace_ref_v<T>, std::string, T&&>; template<typename T> replacement_forward_type_t<T> replace_forward( T& param ) { if constexpr( is_string_ref_v<T> ) { return std::string( "Replaced" ); } else if constexpr( is_char_array_ref_v<T> ) { return std::string( "Replaced" ); } else if constexpr( is_char_pointer_ref_v<T> ) { return std::string( "Replaced" ); } else { return std::forward<T>( param ); } } template<typename ...Params> void f1( Params&&... p ) { } template<typename ...Params> void f2( Params&&... p ) { f1( replace_forward<Params>( p )... ); }
-
@DocShoe sagte in Parameterpack verändern:
Ich denke du hast da noch einen Fehler drin:
template<typename T> replacement_forward_type_t<T> replace_forward( T& param )sollte stattessen:
template<typename T> replacement_forward_type_t<T> replace_forward( T&& param )Du willst hier eine Forwarding Reference haben, ansonsten wird die Referenz (
&oder&&nicht Teil des TypsT). Du machst also kein Forwarding, was du ja offensichtlich vorhast.Ich denke du kannst das noch etwas vereinfachen und auf den
replacement_forward_type_t<T>komplett verzichten. Das was der Typ macht, kann man nämlich auch mitdecltype(auto)erreichen.template<typename T> auto replace_forward(T&& param ) -> decltyle(auto) { if constexpr( is_string_ref_v<T> ) { return std::string( "Replaced" ); } else if constexpr( is_char_array_ref_v<T> ) { return std::string( "Replaced" ); } else if constexpr( is_char_pointer_ref_v<T> ) { return std::string( "Replaced" ); } else { return std::forward<T>( param ); } }oder auch:
template<typename T> decltyle(auto) replace_forward(T&& param ) { ... }Deduktion mit
decltype(auto)erhält die Value Category des zurückgegebenen Wertes. D.h.return std::string("Replaced")gibt einen Wert vom Typstd::string&&zurück undreturn std::forward<T>(param)als was immer T deduziert wurde.
