2010-03-12 14 views
13

considera la siguiente función:stringstream temporal para c_str() en una sola declaración

void f(const char* str); 

Supongamos que quiero generar una cadena usando stringstream y pasarlo a esta función. Si quiero hacerlo en un comunicado, probaría:

f((std::ostringstream() << "Value: " << 5).str().c_str()); // error 

Esto da un error: 'str()' no es un miembro de 'basic_ostream'. OK, entonces el operador < < está regresando ostream en lugar de ostringstream - ¿qué hay de devolverlo a un ostringstream?

1) ¿Es seguro este yeso?

f(static_cast<std::ostringstream&>(std::ostringstream() << "Value: " << 5).str().c_str()); // incorrect output 

Ahora con esto, resulta que para el < < ("Valor:") operador de llamada, en realidad está llamando operador de ostream < < (void *) e imprimir una dirección hexadecimal. Esto está mal, quiero el texto.

2) ¿Por qué el operador < < en el std :: ostringstream() temporal llama al operador ostream? Sin duda, el temporal tiene un tipo de 'ostringstream' no 'ostream'?

¡Puedo lanzar el temporal para forzar la llamada correcta del operador también!

f(static_cast<std::ostringstream&>(static_cast<std::ostringstream&>(std::ostringstream()) << "Value: " << 5).str().c_str()); 

Esto parece trabajar y pasa "Valor: 5" para f().

3) ¿Confío en un comportamiento indefinido ahora? Los moldes parecen inusuales.


yo sepa la mejor alternativa es algo como esto:

std::ostringstream ss; 
ss << "Value: " << 5; 
f(ss.str().c_str()); 

... pero estoy interesado en el comportamiento de hacerlo en una sola línea. Supongamos que alguien quisiera crear una macro (dudosa):

#define make_temporary_cstr(x) (static_cast<std::ostringstream&>(static_cast<std::ostringstream&>(std::ostringstream()) << x).str().c_str()) 

// ... 

f(make_temporary_cstr("Value: " << 5)); 

¿Funcionaría como se esperaba?

+0

duplicado posible de [stringstream, cuerda, y Char \ * confusión conversión] (http://stackoverflow.com/questions/1374468/stringstream-string-and-char-conversion-confusion). – jww

Respuesta

14

No puede convertir la secuencia temporal a std::ostringstream&. Está mal formado (el compilador debe decirle que está mal). Lo siguiente puede hacerlo, sin embargo:

f(static_cast<std::ostringstream&>(
    std::ostringstream().seekp(0) << "Value: " << 5).str().c_str()); 

Eso por supuesto es feo. Pero muestra cómo puede funcionar. seekp es una función miembro que devuelve un std::ostream&.Probablemente mejor escribir esto generalmente

template<typename T> 
struct lval { T t; T &getlval() { return t; } }; 

f(static_cast<std::ostringstream&>(
    lval<std::ostringstream>().getlval() << "Value: " << 5).str().c_str()); 

La razón de que sin nada que tarda el void*, se debe a que operator<< es una función miembro. El operator<< que toma un char const* no lo es.

+0

Su fragmento con seekp (0) devuelve una cadena vacía para mí, pero parece que la plantilla lval funciona correctamente. ¿Eso depende completamente del comportamiento definido entonces, y sería portátil? – AshleysBrain

+0

@ Ashley, seguro que está todo definido. –

+0

¿Podría lval tener un operador de conversión a T &, evitando la llamada a la función? (o tal vez el compilador no recoge ese operador automáticamente ...) – Macke

3

Un temporal no se puede pasar como una referencia no constante a una función, es por eso que no encuentra el operador de transmisión correcto y toma el que tiene el argumento void * (es una función miembro y lo llama en un temporal está bien).

Lo que se refiere a superar las limitaciones por el casting, tengo la sensación de que es, de hecho, UB, pero no puedo decirlo con certeza. Alguien más seguramente citará el estándar.

+0

No estoy pasando el ostringstream temporal a f() - Solo estoy pasando el resultado de c_str(), que es const (si es importante). Además, aun así, ¿qué hay con eso significa que elige el argumento nulo? ¿Qué tiene de diferente eso que le permite ser escogido pero no el argumento const char *? – AshleysBrain

+2

Está intentando pasar un ostringstream temporal a ostream & operator << (ostream &, char const *) y eso falla (sin el yeso que creó para arreglarlo). ostream & ostream :: operator << (void const *) se puede seleccionar porque es una función miembro y se permite la función de funciones de miembros temporales. – Tronic

0

Si te gusta declaraciones one_lined puede escribir:

// void f(const char* str); 
f(static_cast<ostringstream*>(&(ostringstream() << "Value: " << 5))->str()); 

Sin embargo, usted debe preferir más fácil de mantener código como:

template <typename V> 
    string NumberValue(V val) 
    { 
    ostringstream ss; 
    ss << "Value: " << val; 
    return ss.str(); 
    } 
f(NumberValue(5)); 
+0

El texto único que ha sugerido es incorrecto y no funcionaría en todos los casos. – missingfaktor

0

utilizo algo un poco como esta para el registro.

#include <sstream> 

using namespace std; 

const char *log_text(ostringstream &os, ostream &theSame) 
{ 
    static string ret; // Static so it persists after the call 
    ret = os.str(); 
    os.str(""); // Truncate so I can re-use the stream 
    return ret.c_str(); 
} 


int main(int argc, char **argv) 
{ 
    ostringstream ss; 
    cout << log_text(ss, ss << "My first message") << endl; 
    cout << log_text(ss, ss << "Another message") << endl; 
} 

Salida:

Mi primer mensaje

Otro mensaje

+1

¿Por qué convertir a 'const char *' cuando puede insertar 'std :: string' en una secuencia? – quantum

1

Esto se puede hacer usando una función lambda C++ 11.

#include <iostream> 
#include <sstream> 

void f(const char * str) 
{ 
    std::cout << str << std::endl; 
} 

std::string str(void (*populate)(std::ostream &)) 
{ 
    std::ostringstream stream; 
    populate(stream); 
    return stream.str(); 
} 

int main(int argc, char * * args) 
{ 
    f(str([](std::ostream & ss){ ss << "Value: " << 5; }).c_str()); 
    return 0; 
} 

// g++ -std=c++11 main.cpp -o main 
// ./main 
// Value: 5 
Cuestiones relacionadas