2017-03-14 8 views
3

Ich versuche, einige Template-Funktionen zu schreiben, die entweder ein std::basic_string oder ein char-Array akzeptieren, aus dem die basic_string konstruiert werden könnte.Vorlage Abzug Anleitung für Funktion?

Meine aktuelle Lösung ist:

#include <string> 

template<typename CharT> 
void foo(std::basic_string<CharT> str) 
{ 
    (void)str; // do something with str 
} 
template<typename CharT> 
void foo(CharT const * arr) 
{ 
    return foo(std::basic_string<CharT>{arr}); 
} 

int main(void) 
{ 
    foo("hello"); 
    foo(std::string{ "hello" }); 
    foo(L"hello"); 
    foo(std::wstring{ L"hello" }); 
} 

Das bedeutet aber, dass ich für jede Funktion eine andere Funktion zu schreiben, die die erste nennt. Das ist ziemlich nervig; Gibt es einen einfacheren Weg? Vielleicht könnte es ein Vorlagen-Deduktionsleitfaden sein, aber soweit ich weiß, existiert er nicht für Funktionen, nur für Klassen.

Die erste Template-Funktion ist nicht ausreichend, weil die Vorlagenableitung fehlschlägt: Der Compiler kann CharT in std::basic_string<CharT> von CharT const * nicht ableiten. Deshalb brauche ich einen einfacheren Weg dies dem Compiler mitzuteilen.

+2

Ersetzen Sie beide Überladungen mit einem einzigen, 'Vorlage void foo (std :: basic_string_view str)'? – ildjarn

+1

@ildjarn Das gleiche Problem, der Vorlagenabzug schlägt fehl. – Boiethios

+0

@davidhigh Ich dachte darüber nach, aber wie man überprüft, ob ein Typ 'basic_string' ist? – Boiethios

Antwort

4

Nach etwas mehr Forschung, imo die beste Option ist, um die C++ 17-Funktion std::basic_string_view verwenden: unter

template<typename CharT> 
void foo(std::basic_string_view<CharT> str) 
{ 
    (void)str; // do something with str ... 
       // while remembering that string_view does not own the string 
} 

Also vergessen Sie die ältere Erklärung, wenn Sie Zugriff auf einen C++ 17 -Compiler.



Es gibt zwei Fälle hier zu berücksichtigen. Der erste Fall ist, dass Sie nicht wirklich etwas mit der grundlegenden Zeichenfolge tun möchten, sondern Sie wenden nur Methoden an, die auch für das char -array verfügbar sind (und wollen nur sicherstellen, dass es unabhängig von den Parametern korrekt aufgerufen wird). In diesem Fall würde ich einfach einen allgemeinen Template-Parameter verwenden:

template<typename string_type 
     /* possibly some SFINAE to allow/disallow certain types */> 
auto foo(string_type s) 
{ 
    std::cout << s << std::endl; 
} 

Zweiten Fall ist, dass Sie wirklich eine besondere Operation auf der Saite tun wollen, das für den char-Array nicht vorhanden ist. In diesem Fall benötigen Sie eine Überladung für basic_string, aber Sie möchten wahrscheinlich nur einmal schreiben und nicht für jede einzelne Funktion, die Sie verwenden. Dies ist, was die folgende string_invoker Klasse zu tun versucht (aber es muss noch eine gewisse Verbesserung, nur daran zu arbeiten):

template<typename method> 
struct string_invoker_impl 
{ 
    string_invoker_impl(method m) : m(m) {} 

    template<typename CharT> 
    auto operator()(std::basic_string<CharT> str) const 
    { 
     return m(str); 
    } 

    template<typename CharT> 
    auto operator()(CharT const * arr) const 
    { 
     return operator()(std::basic_string<CharT>{arr}); 
    } 

    //possibly further methods for non-const array's, modification, etc.  

    method m; 
}; 

auto string_invoker = [](auto m) { return string_invoker_impl<decltype(m)>{m}; }; 

auto foo_impl = [](auto str) {std::cout<< str <<std::endl; }; 
auto foo = string_invoker(foo_impl); 

//you can merge the previous two calls also in a single one: 
//auto foo = string_invoker([](auto str) {std::cout<< str <<std::endl; }); 


int main(void) 
{ 
    foo("hello"); 
    foo(std::string{ "hello" }); 
    //foo(L"hello");      //need std::wcout, thus it fails with std::cout 
              //but it's no general problem, just overload your foo_impl function 
    //foo(std::wstring{ L"hello" }); 
} 

DEMO

+0

Wow, sehr schlau. Ich werde damit spielen, wenn ich Zeit habe (ich bin jetzt im Büro). – Boiethios

+0

Eine mögliche Verbesserung wäre, die Zeigerüberladung auf char-Typen zu beschränken (vielleicht durch SFINAE mit 'std :: char_traits'?) – Rerito

+1

@Rerito: könnte sein, aber ich würde denken, dass die Konstruktion von' std :: basic_string' dann trotzdem fehlschlägt . – davidhigh

2

nur den sauren Apfel beißen und 2 Überlastungen verwenden. Jede clevere Lösung (welche wie davidhigh gezeigt hat existiert) wird nur unnötige Komplexität, potentielle Fehler und Verwirrung für den nächsten Leser hinzufügen.

Sie schreiben nur einmal, lesen aber mehrfach. Der kleine Incovenient des Schreibens einer 1 Linie der Körperüberlastung ist es wert, gegen eine nicht-idiomatische verschachtelte intelligente Weise zu tun.

Versteh mich nicht falsch, ich liebe diese intelligente Lösungen in C++ zu finden, aber wenn ich diese Lösung in einem Produktionscode fand, würde es mich ein paar gute Minuten dauern, nur um herauszufinden, was zum Teufel es ist und was macht es, nur um herauszufinden, dass es einfach nur das tut, was auf eine komplexe Art eine sehr einfache Sache hätte sein sollen, würde ich ... naja, sagen wir mal, ich würde nicht nette Dinge über den Autor des Codes sagen. Faul beim Schreiben von Code kostet Sie manchmal, wenn Sie den Code pflegen, debuggen, erweitern oder sogar verwenden.

Schreiben Sie einfachen, idiomatischen und leicht verständlichen Code!

+0

sehr gültig, danke. – davidhigh

+0

Ich denke, das ist die beste Lösung in der Produktion, aber ich akzeptierte die andere Antwort, weil es auf das Problem antwortet. – Boiethios