stoi numeros long float convertir como c++ c++11 stringstream string-conversion

numeros - string to float c++



C++ convierte valores simples a cadena (6)

En este momento, uso el siguiente fragmento de código para convertir dummily tipos básicos ( int , long , char[] , este tipo de cosas) a std::string para su posterior procesamiento:

template<class T> constexpr std::string stringify(const T& t) { std::stringstream ss; ss << t; return ss.str(); }

sin embargo, no me gusta el hecho de que dependa de std::stringstream . Intenté usar std::to_string (del repertorio de C ++ 11) sin embargo, se atraganta con las variables char[] .

¿Hay una manera simple de ofrecer una solución elegante para este problema?


¿Hay una manera simple de ofrecer una solución elegante para este problema?

Como nadie lo propuso, considere usar boost::lexical_cast .

Esto se integra a la perfección con cualquier cosa que implemente el operador std :: ostream << y se puede extender para tipos personalizados.


Aunque la pregunta no es del tipo de código dado, ya que ya tengo una solución implementada, pensé en compartirla:

template <class... Tail> inline auto buildString(std::string const &head, Tail const &... tail) -> std::string; template <class... Tail> inline auto buildString(char const *head, Tail const &... tail) -> std::string; template <class... Tail> inline auto buildString(char *head, Tail const &... tail) -> std::string; template <class Head, class... Tail> inline auto buildString(Head const &head, Tail const &... tail) -> std::string; inline auto buildString() -> std::string { return {}; } template <class... Tail> inline auto buildString(std::string const &head, Tail const &... tail) -> std::string { return head + buildString(tail...); } template <class... Tail> inline auto buildString(char const *head, Tail const &... tail) -> std::string { return std::string{head} + buildString(tail...); } template <class... Tail> inline auto buildString(char *head, Tail const &... tail) -> std::string { return std::string{head} + buildString(tail...); } template <class Head, class... Tail> inline auto buildString(Head const &head, Tail const &... tail) -> std::string { return std::to_string(head) + buildString(tail...); }

Uso:

auto gimmeTheString(std::string const &str) -> void { cout << str << endl; } int main() { std::string cpp_string{"This c++ string"}; char const c_string[] = "this c string"; gimmeTheString(buildString("I have some strings: ", cpp_string, " and ", c_string, " and some number ", 24)); return 0; }


Creo que la solución más elegante es:

#include <string> template <typename T> typename std::enable_if<std::is_constructible<std::string, T>::value, std::string>::type stringify(T&& value) { return std::string(std::forward<T>(value)); // take advantage of perfect forwarding } template <typename T> typename std::enable_if<!std::is_constructible<std::string, T>::value, std::string>::type stringify(T&& value) { using std::to_string; // take advantage of ADL (argument-dependent lookup) return to_string(std::forward<T>(value)); // take advantage of perfect forwarding }

Aquí, si podemos construir std::string usando T (lo verificamos con la ayuda de std::is_constructible<std::string, T> ), entonces lo hacemos, de lo contrario usamos to_string .

Por supuesto, en C ++ 14 puede reemplazar typename std::enable_if<...>::type con mucho más corto std::enable_if_t<...> . Un ejemplo está en la versión más corta del código, justo debajo.

La siguiente es una versión más corta , pero es un poco menos eficiente, porque necesita un movimiento adicional de std::string (pero si hacemos una copia, es incluso menos eficiente):

#include <string> std::string stringify(std::string s) { // use implicit conversion to std::string return std::move(s); // take advantage of move semantics } template <typename T> std::enable_if_t<!std::is_convertible<T, std::string>::value, std::string> stringify(T&& value) { using std::to_string; // take advantage of ADL (argument-dependent lookup) return to_string(std::forward<T>(value)); // take advantage of perfect forwarding }

Esta versión usa la conversión implícita a std::string posible, y usa to_string contrario. Observe el uso de std::move para aprovechar la semántica de movimientos de C ++ 11.

Aquí es por qué mi solución es mejor que la solution actualmente más votada por @cerkiewny:

  • Tiene una aplicabilidad mucho más amplia, ya que, gracias a ADL , también se define para cualquier tipo para el que se define la conversión con la función to_string (no solo la versión to_string del mismo), consulte el ejemplo de uso a continuación. Mientras que la solución de @cerkiewny solo funciona para los tipos fundamentales y para los tipos a partir de los cuales es posible construir std :: string.

    Por supuesto, en su caso es posible agregar sobrecargas adicionales de stringify para otros tipos, pero es una solución mucho menos sólida si se compara con agregar nuevas versiones ADL de to_string . Y es probable que la altura sea, que to_string compatible con to_string ya está definido en una biblioteca de terceros para un tipo que queremos usar. En este caso, con mi código no es necesario que escriba ningún código adicional para que stringify funcione.

  • Es más eficiente, porque aprovecha el reenvío perfecto de C ++ 11 (mediante el uso de referencias universales ( T&& ) y std::forward ).

Ejemplo de uso:

#include <string> namespace Geom { class Point { public: Point(int x, int y) : x(x), y(y) {} // This function is ADL-compatible and not only ''stringify'' can benefit from it. friend std::string to_string(const Point& p) { return ''('' + std::to_string(p.x) + ", " + std::to_string(p.y) + '')''; } private: int x; int y; }; } #include <iostream> #include "stringify.h" // inclusion of the code located at the top of this answer int main() { double d = 1.2; std::cout << stringify(d) << std::endl; // outputs "1.200000" char s[] = "Hello, World!"; std::cout << stringify(s) << std::endl; // outputs "Hello, World!" Geom::Point p(1, 2); std::cout << stringify(p) << std::endl; // outputs "(1, 2)" }

Enfoque alternativo, pero no recomendado.

También consideré la sobrecarga de to_string :

template <typename T> typename std::enable_if<std::is_constructible<std::string, T>::value, std::string>::type to_string(T&& value) { return std::string(std::forward<T>(value)); // take advantage of perfect forwarding }

Y una versión más corta que usa la conversión implícita a std::string :

std::string to_string(std::string s) { // use implicit conversion to std::string return std::move(s); // take advantage of move semantics }

Pero estos tienen serias limitaciones: debemos recordar escribir to_string lugar de std::to_string todos los lugares donde queremos usarlo; también es incompatible con el patrón de uso de ADL más común:

int main() { std::string a = std::to_string("Hello World!"); // error using std::to_string; // ADL std::string b = to_string("Hello World!"); // error }

Y es más probable, hay otros problemas relacionados con este enfoque.


La solución más sencilla es la sobrecarga para los tipos que desea:

using std::to_string; template<size_t Size> std::string to_string(const char (&arr)[Size]) { return std::string(arr, Size - 1); }

Ya que to_string no es una plantilla, no puedes especializarla, pero afortunadamente esto es más fácil.

El código supone que la matriz está terminada en nulo, pero sigue siendo segura si no lo está.

También es posible que desee colocar la línea de using dentro de las funciones que llaman a to_string si tiene fuertes sentimientos sobre dónde pertenece el using .

Esto también tiene la ventaja de que si se le pasa una cadena no terminada en nulo de alguna manera, no tiene UB como lo hace el argumento std::string constructor.


Por lo que sé, la única forma de hacerlo es especializando la plantilla por el tipo de parámetro con SFINAE.

Necesitas incluir las type_traits.

Entonces en lugar de tu código usa algo como esto:

template<class T> typename std::enable_if<std::is_fundamental<T>::value, std::string>::type stringify(const T& t) { return std::to_string(t); } template<class T> typename std::enable_if<!std::is_fundamental<T>::value, std::string>::type stringify(const T& t) { return std::string(t); }

esta prueba funciona para mi

int main() { std::cout << stringify(3.0f); std::cout << stringify("Asdf"); }

Nota importante: ¡las matrices de caracteres que se pasaron a esta función deben terminarse en nulo!

Como se indica en los comentarios de yakk, puede deshacerse de la terminación nula con:

template<size_t N> std::string stringify( char(const& s)[N] ) { if (N && !s[N-1]) return {s, s+N-1}; else return {s, s+N}; }


Recomiendo usar enable_if_t y si va a tomar alguna de las variables de un solo carácter, las especializa:

template<typename T> enable_if_t<is_arithmetic<T>::value, string> stringify(T t){ return to_string(t); } template<typename T> enable_if_t<!is_arithmetic<T>::value, string> stringify(T t){ return static_cast<ostringstream&>(ostringstream() << t).str(); } template<> string stringify<char>(char t){ return string(1, t); }

Aquí sólo estoy especializando char . Si necesita especializarse en wchar , char16 o char32 también deberá hacerlo.

De todos modos, para los tipos no aritméticos, estas sobrecargas usarán de forma predeterminada el uso de ostringstream cual es una buena causa si ha sobrecargado el operador de extracción para una de sus clases, esto lo manejará.

Para los tipos aritméticos esto usará to_string , con la excepción de char y cualquier otra cosa que sobrecargue, y esos pueden crear directamente una string .

Editar:

Dyp sugirió usar si to_string acepta un argumento de T::type como mi condición enable_if_t .

La solución más simple solo está disponible para usted si tiene acceso a is_detected en #include <experimental/type_traits> . Si solo definas:

template<typename T> using to_string_t = decltype(to_string(declval<T>()));

Luego puedes configurar tu código como:

template<typename T> decltype(to_string(T{})) stringify(T t){ return to_string(t); } template<typename T> enable_if_t<!experimental::is_detected<to_string_t, T>::value, string> (T t){ return static_cast<ostringstream&>(ostringstream() << t).str(); } template<> string stringify<char>(char t){ return string(1, t); }

Hice esta pregunta para descubrir cómo usar to_string como mi condición. Si no tiene acceso a is_detected , le recomiendo que lea algunas de las respuestas porque son fenomenales: Metaprogramación: falla en la definición de la función La definición define una función separada