c++ - declared - to_string is not a member of std
Haciendo una clase definida por el usuario std:: to_string(able) (6)
Aquí hay una solución alternativa. Nada necesariamente más elegante o demasiado elegante, pero es un enfoque alternativo. Se supone que todas las clases a las que pretende llamar to_string on tienen una función ToString ().
Aquí hay una plantilla de función que solo funcionará con objetos de tipo de clase y llamará a una función ToString ().
template<typename T, typename = std::enable_if_t<std::is_class<T>::value>>
std::string to_string(const T& t) {
return t.ToString();
}
Tal vez queremos que funcione también con std :: string.
template<>
std::string to_string(const std::string& t) {
return t;
}
Aquí hay un ejemplo del código en uso. Tenga en cuenta el espacio de nombres ficticios to_s. Supongo que si pones usando std :: to_string en la función principal, entra en el nombre de nuestra función de plantilla, por lo que tenemos que introducir el nombre indirectamente de esta manera. Si alguien sabe la forma correcta de hacerlo, agradecería un comentario.
#include <cstring>
#include <iostream>
#include <string>
#include <type_traits>
union U {
double d;
const char* cp;
};
struct A {
enum State { kString, kDouble };
State state;
U u;
void Set(const char* cp) {
u.cp = cp;
state = kString;
}
std::string ToString() const {
switch (state) {
case A::kString : return std::string(u.cp); break;
case A::kDouble : return std::to_string(u.d); break;
default : return "Invalid State";
}
}
};
namespace to_s { using std::to_string; };
int main() {
using namespace to_s;
std::string str = "a nice string";
double d = 1.1;
A a { A::kDouble, {1.2} };
std::cout << "str: " << to_string(str) << ", d: " << to_string(d) << std::endl;
std::cout << "a: " << to_string(a) << std::endl;
a.Set(str.c_str());
std::cout << "a: " << to_string(a) << std::endl;
std::memset(&a, ''i'', sizeof(a));
std::cout << "a: " << to_string(a) << std::endl;
}
Esto es lo que tengo:
str: una buena cadena, d: 1.100000
a: 1.200000
a: una buena cadena
a: Estado inválido
Sé que parece demasiado Java o C #. Sin embargo, ¿es posible / bueno / sabio que mi propia clase sea válida como entrada para la función std::to_string
? Ejemplo:
class my_class{
public:
std::string give_me_a_string_of_you() const{
return "I am " + std::to_string(i);
}
int i;
};
void main(){
my_class my_object;
std::cout<< std::to_string(my_object);
}
Si no existe tal cosa (y creo que), ¿cuál es la mejor manera de hacerlo?
Cuál es la "mejor" manera es una pregunta abierta.
Hay algunas maneras.
Lo primero que hay que decir es que no se permite la sobrecarga std::to_string
para un tipo personalizado. Solo podemos especializar funciones y clases de plantillas en el espacio de nombres std::to_string
para tipos personalizados, y std::to_string
no es una función de plantilla.
Dicho esto, una buena manera de tratar la to_string
es como un operador o una implementación de swap
. es decir, permitir la búsqueda dependiente del argumento para hacer el trabajo.
así que cuando queremos convertir algo en una cadena podríamos escribir:
using std::to_string;
auto s = to_string(x) + " : " + to_string(i);
asumiendo que x era un objeto de tipo X en el espacio de nombres Y e i era un int, entonces podríamos definir:
namespace Y {
std::string to_string(const X& x);
}
lo que ahora significaría que:
La invocación a to_string(x)
realidad selecciona Y::to_string(const Y::X&)
, y
Al invocar to_string(i)
selecciona std::to_string(int)
Yendo más allá, puede ser que desee que la cadena haga lo mismo que el operador <<, entonces uno puede escribirse en términos del otro:
namespace Y {
inline std::ostream& operator<<(std::ostream& os, const X& x) { /* implement here */; return os; }
inline std::string to_string(const X& x) {
std::ostringstream ss;
ss << x;
return ss.str();
}
}
No puede agregar nuevas sobrecargas de to_string
en el std
nombres to_string
, pero puede hacerlo en su espacio de nombres:
namespace my {
using std::to_string;
std::string to_string(const my_class& o) {
return o.give_me_a_string_of_you();
}
}
Entonces puedes usar my::to_string
para todos los tipos.
int main()
{
my_class my_object;
std::cout << my::to_string(my_object);
std::cout << my::to_string(5);
}
Primero, un poco de ayuda ADL:
namespace notstd {
namespace adl_helper {
using std::to_string;
template<class T>
std::string as_string( T&& t ) {
return to_string( std::forward<T>(t) );
}
}
template<class T>
std::string to_string( T&& t ) {
return adl_helper::as_string(std::forward<T>(t));
}
}
notstd::to_string(blah)
realizará una búsqueda ADL de to_string(blah)
con std::to_string
en alcance.
Luego modificamos tu clase:
class my_class{
public:
friend std::string to_string(my_class const& self) const{
return "I am " + notstd::to_string(self.i);
}
int i;
};
y ahora notstd::to_string(my_object)
encuentra la to_string
adecuada, al igual que notstd::to_string(7)
.
Con un poco más de trabajo, incluso podemos admitir los métodos .tostring()
en los tipos que se detectarán y utilizarán automáticamente.
Probablemente solo quieras sobrecargar al operator<<()
algo como:
std::ostream& operator << ( std::ostream& os, const my_class& rhs ) {
os << "I am " << rhs.i;
return os;
}
Alternativamente:
std::ostream& operator << ( std::ostream& os, const my_class& rhs ) {
os << rhs.print_a_string();
return os;
}
Entonces simplemente puedes hacer:
int main() {
my_class my_object;
std::cout << my_object;
return 0;
}
Puede definir su propia to_string
en su propio espacio de nombres (por ejemplo, foo
).
namespace foo {
std::string to_string(my_class const &obj) {
return obj.string give_me_a_string_of_you();
}
}
Y utilízalo como:
int main(){
my_class my_object;
std::cout<< foo::to_string(my_object);
}
Desafortunadamente, no puede definir su propia versión de to_string
en namespace std
porque está de acuerdo con el estándar 17.6.4.2.1 Namespace std [namespace.std] (Emphasis Mine) :
El comportamiento de un programa de C ++ no está definido si agrega declaraciones o definiciones al espacio de nombres estándar o a un espacio de nombres dentro del espacio de nombres estándar, a menos que se especifique lo contrario. Un programa puede agregar una especialización de plantilla para cualquier plantilla de biblioteca estándar al espacio de nombres estándar solo si la declaración depende de un tipo definido por el usuario y la especialización cumple con los requisitos de biblioteca estándar para la plantilla original y no está explícitamente prohibida.