Vorlagenfreundliche Zeichenfolge zu numerisch in C ++


48

In der C ++ - Standardbibliothek gibt es Funktionen zum Konvertieren von Zeichenfolgen in numerische Typen:

stoi
stol
stoll
stoul
stoull
stof
stod
stold

aber ich finde es mühsam, sie im Vorlagencode zu verwenden. Warum gibt es keine Vorlagenfunktionen wie:

template<typename T>
T sto(...)

Zeichenfolgen in numerische Typen konvertieren?

Ich sehe keinen technischen Grund, sie nicht zu haben, aber vielleicht fehlt mir etwas. Sie können darauf spezialisiert sein, die zugrunde liegenden benannten Funktionen aufzurufen und nicht numerische Typen zu verwenden enable_if/ conceptszu deaktivieren.

Gibt es in der Standardbibliothek vorlagenfreundliche Alternativen, um Zeichenfolgen effizient in numerische Typen umzuwandeln und umgekehrt?



1
@Boiethios nicht wirklich - die Antworten auf diese Frage erklären die Gründe für das "Warum", aber sie kommen nicht mit praktischen Lösungen wie der akzeptierten Antwort. Ich habe meine Frage bearbeitet, um nach einer Alternative zu fragen, um besser anzugeben, was ich brauche
Mircea Ispas

Antworten:


40

Warum gibt es keine Vorlagenfunktionen wie:

C ++ 17 hat eine solche generische String-to-Number-Funktion, die jedoch anders benannt ist. Sie gingen mit std::from_chars, was für alle numerischen Typen überladen ist.

Wie Sie sehen können, nimmt die erste Überladung einen beliebigen Ganzzahltyp als Ausgabeparameter und weist ihm nach Möglichkeit den Wert zu.

Es kann folgendermaßen verwendet werden:

template<typename Numeric>
void stuff(std::string_view s) {
    auto value = Numeric{};

    auto [ptr, error] = std::from_chars(s.data(), s.data() + s.size(), value);

    if (error) {
        // error with the conversion
    } else {
        // conversion successful, do stuff with value
    }
}

Wie Sie sehen können, kann es im generischen Kontext funktionieren.



1
Na sicher! Es funktioniert sogar mit einfachen Strukturen oder, wenn die richtige Schnittstelle gegeben ist, auch mit Klassen.
Guillaume Racicot

13

Es ist keine Vorlage und funktioniert nicht mit Gebietsschemas. Wenn dies jedoch kein Show-Stopper ist, hat C ++ 17 bereits das, was Sie wollen: std::from_chars

Es gibt Überladungen für alle Ganzzahl- und Gleitkommatypen, und die Schnittstelle ist bis auf die letzten Parameter, die für die Ganzzahl- bzw. Gleitkommatypen unterschiedlich sind, identisch (wenn die Standardeinstellung jedoch in Ordnung ist, müssen Sie dies nicht tun etwas ändern). Da dies keine länderbezogene Funktion ist, ist sie auch recht schnell. Es schlägt jede andere Funktion zur Umwandlung von Zeichenfolgen in Werte und ist im Allgemeinen um Größenordnungen.

Es gibt ein sehr gutes CPPCON-Video über Stephan T. Lavavej <charconv>(in dem der Header from_charslebt), das Sie hier über seine Verwendung und Leistung sehen können: https://www.youtube.com/watch?v=4P_kbF0EbZM


1
@ NathanOliver: stoiund seine Freunde (die in der Frage erwähnten Conversions) funktionieren auch nicht mit Gebietsschemas, das ist also kein Showstopper.
Pete Becker

9

Sie würden nicht viel gewinnen, weil in einem Ausdruck wie

int x = sto("1");

Es gibt keine (einfache) Möglichkeit, den gewünschten Typ für den Vorlagenparameter abzuleiten. Du müsstest schreiben

int x = sto<int>("1");

was in gewissem Maße den Zweck der Bereitstellung einer generischen Funktion zunichte macht. Auf der anderen Seite a

template<typename T>
void sto(std::string x,T& t);

wäre von Nutzen, wie Sie erkannt haben. In C ++ 17 gibt es etwas std::from_chars, das mehr oder weniger genau das tut (es ist keine Vorlage, sondern eine Reihe von Überladungen, und es werden Zeiger auf Zeichen anstelle einer Zeichenfolge verwendet, aber das sind nur geringfügige Details).

PS Es gibt keine einfache Möglichkeit, den gewünschten Typ im obigen Ausdruck abzuleiten, aber es gibt eine Möglichkeit. Ich glaube nicht, dass der Kern Ihrer Frage genau die Signatur war, nach der Sie gefragt haben, und ich denke nicht, dass das Folgende eine gute Möglichkeit ist, sie zu implementieren, aber ich wusste, dass es eine Möglichkeit gibt, die oben genannten zu int x = sto("1");kompilieren, und ich war neugierig, sie zu sehen in Aktion.

#include <iostream>
#include <string>

struct converter {
    const std::string& x;
    template <typename T> operator T() { return 0;}
};

template <> converter::operator int() { return stoi(x); }
template <> converter::operator double() { return stod(x); }
converter sto(const std::string& x) { return {x}; }

int main() {
    std::string s{"1.23"};
    int x = sto(s);
    double y = sto(s);
    std::cout << x << " " << y;
}

Dies funktioniert wie beabsichtigt, hat aber schwerwiegende Nachteile, vielleicht am wichtigsten ist, dass es das Schreiben ermöglicht auto x = sto(s);, dh es ist leicht falsch zu verwenden.


Ich halte es für eine gute Idee, sich hier auf die implizite Konvertierung zu verlassen. Der Versuch, Auto zu deaktivieren, ist jedoch ein Problem. Normalerweise habe ich dies gesehen, indem eine private const-Referenz in eine Klasse eingefügt wurde, die nur mit gültigen Methoden initialisiert wird. Ich kann nicht sehen, wie man das hier nutzen würde, weil wir ein ganzes Konverterobjekt konstruieren müssen, bevor wir fortfahren. Hmmm ....
bremen_matt

Ich kann trotz des nicht abgeleiteten Typparameters einen Wert sehen. Wie die Frage besagt, besteht die Motivation darin, den Vorlagencode verwenden zu können, in den Sie in einen Typ konvertieren, der zwischen den Instanziierungen variiert.
Toby Speight

Was ist das grundlegende Problem mit auto x = sto(s)? Diese spezielle Implementierung bricht ab, da converter::xes sich um eine Referenz handelt, die außerhalb des Gültigkeitsbereichs liegt, die jedoch behoben werden kann. Entfernen Sie einfach die Referenz und verlassen Sie sich auf std::stringdie Verschiebungssemantik.
MSalters

@ MSalters Ja, es war die Referenz, die ich für problematisch hielt, aber Sie haben Recht, Sie müssen keine Referenz verwenden. Was mich tatsächlich mehr stört, ist, dass es eine Funktion zu sein scheint, aber die eigentliche Funktionalität ist vorhanden converter. Außerdem bin ich mir nicht sicher, ob die Verwendung eines Vorlagenkonvertierungsoperators die beste Wahl war, Dinge, die behoben werden könnten. Vielleicht ist es nicht so schlimm, wie ich ursprünglich dachte
idclev 463035818

Ich glaube nicht, dass es hier ein Problem mit der const-Referenz gibt. Ich verstehe, dass die const-Referenz die Lebensdauer des Strings bis zur Zerstörung des Konverters beibehält ( herbsutter.com/2008/01/01/… )
bremen_matt


3

In älteren C ++ - Versionen ist stringstream Ihr Freund. Wenn ich das richtig verstehe, könnte das Folgende für Sie interessant sein. Es ist C ++ 11.

https://wandbox.org/permlink/nUNiUwWWTr7a0NXM

#include <sstream>
#include <string>
#include <iostream>

template<typename T, typename String>
T sto(const String & str) {
    T val;
    std::stringstream ss(str);
    ss >> val;
    return val;
}

template<typename T, typename String>
void sto(const String & str, T & val) {
    std::stringstream ss(str);
    ss >> val;
}

int main() {   
    std::cout << sto<float>("1.1") << ", " << sto<int>(std::string{"2"});

    // An alternative version that infers the type 
    double d;
    sto("3.3", d);
    std::cout << ", " << d;
}

Diese Methode funktioniert in C ++ 11 und ist ziemlich allgemein. Nach meiner Erfahrung ist diese Methode robust, aber nicht die leistungsfähigste.


Ja, das habe ich verwendet, aber die Leistung ist unten aufgeführt, Funktionen, die manchmal nicht erwünscht sind
Mircea Ispas
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.