2012-04-11 16 views
5

Tengo un par de preguntas que creo que será bastante fácil para alguien con experiencia en C++ para responder, me atrevo las preguntas para el TL; DRC-Style strings a std :: cadena conversión aclaración

Dado el siguiente código:

void stringTest(const std::string &s) 
{ 
    std::cout << s << std::endl; 
} 

int main() 
{ 
    stringTest("HelloWorld"); 
} 

Hopefuly alguien puede señalar el error en mi proceso de pensamiento aquí:

¿por qué el parámetro en stringTest tienes que estar marcada const cuando pasa una cadena C-Style? ¿No hay una conversión implícita a std :: string que tenga lugar usando su constructor de cadena cstyle, por lo tanto "s" ya no es una referencia a un literal (y no es necesario que sea const).

Por otra parte, lo que sería un aspecto constructor de cadena cstyle similar, y cómo el compilador sabe a invocar este al ver:

stringTest("HelloWorld"); 

¿Es, simplemente reconocer una cadena literal a ser algo así como un char * ?

He tropezado con estas preguntas mientras estudiaba los constructores de copia. Otra quesiton rápida para mi propia aclaración ...

En el caso de algo así como:

std::string s = "HelloWorld"; 

es el constructor de cadena cstyle utilizado para crear instancias de un std :: string temporal, y luego la cadena temporal es copiado en "s" usando el constructor de copia de cadena?:

std::string(const std::string&); 
+0

"¿Por qué el parámetro en stringTest tiene que marcarse const"? Independientemente de cuando sea _necessary_, quiere hacer _all_ referencias 'const' que en realidad no se modifican. – leftaroundabout

+0

@leftaroundabout: Punto justo, sin duda tomaré esto en serio. – Riken

+0

posible duplicado de [¿Cómo es que una referencia no constante no puede vincularse a un objeto temporal?] (Http://stackoverflow.com/questions/1565600/how-come-a-non-const-reference-cannot-bind-to -a-temporary-object) –

Respuesta

2

¿Por qué el parámetro en stringTest tienes que estar marcada const cuando se pasa una cadena C-Style?

Sólo tiene que cuando el parámetro es una referencia, ya que un temporal std::string se construye a partir del char const* se pasa en un país que no const y referencia a un temporal es ilegal.

¿Simplemente reconoce una cadena literal para ser algo así como un char *?

Una cadena literal es una matriz char const, que se descompone en char const*. A partir de ahí, el compilador infiere que debe usar el constructor explicitstd::string::string(char const *) para construir el temporal.

es el constructor cstyle utilizado para crear instancias de un std :: string temporal, y luego la cadena temporal se copia en "s" con el constructor copia de cadena?

Es un poco más complicado que eso. Sí, se crea un temporal. Pero el constructor de copia puede o no ser llamado; el compilador puede omitir la construcción de copia como una optimización.El constructor de copia aún debe ser proporcionado, sin embargo, por lo que el siguiente no se compilará:

class String { 
    String(char const *) {} 
    private: 
    String(String const &); 
}; 

int main() 
{ 
    String s = ""; 
} 

También, en C++ 11 el movimiento constructor será utilizado, si lo hubiere; en ese caso, el constructor de copia no es necesario.

+0

+1 pero podría (debería) aclarar que en el tercer caso, aunque el constructor de copia es * obligatorio *, en realidad no se invoca porque (siempre - independientemente de la optimización?) se eliminará . Esto es bastante importante a pesar de que es "solo" una optimización permitida. –

+0

Una cadena literal no tiene 'char const *', es 'char const [N]', que por supuesto es implícitamente convertible a 'char const *' –

+0

@KonradRudolph: lo hizo y se expandió un poco en la copia/movimiento cosa de constructor –

0

En este ejemplo, se requiere const string & s para invocar al constructor desde el argumento "HelloWorld". El constructor utilizado es la construcción de conversión de tipo.

A string& s no funciona porque s hace referencia directa a un objeto de cadena.

La conversión de tipo se define por algo similar a

basic_string(const _CharT* __s); 

Con un typedef

typedef basic_string<char> string; 

Así que la declaración sería evaluar a

basic_string(const char * __s)  
+0

Eso * suena * incorrecto. O estás equivocado o la explicación no está clara. De cualquier forma, 'const' * no * es requerido para invocar un constructor de copia. –

+0

@KonradRudolph Parecía mal porque estaba mal, mi límite de pensamiento estaba en retroceso. –

2

¿Por qué el parámetro en stringTest debe marcarse const cuando pasó una cadena C-Style?

EDITAR: Los temporarios deben ser inmutables. Ver comentario y respuesta de larsmans, él tiene razón.

sencilla razón:

void change(std::string& c) { c = "abc"; } 
change("test"); // what should the code exactly do?? 

Por otra parte, lo que haría un aspecto constructor de cadena cstyle similar, y cómo el compilador sabe a invocar este al ver:

Se levanta la std::string de string(char*) constructor

En el caso de algo ing como:

std::string s = "HelloWorld"; 

es el constructor cstyle utilizado para crear instancias de un std :: string temporal, y luego la cadena temporal se copia en "s" con el constructor copia de cadena ?: std :: string (const std :: cadena &);

No. En este caso (TYPE variable = SOMETHING), es lo mismo que escribir TYPE variable(SOMETHING);. Entonces, no se usa copia.

+0

Su primer y tercer punto son incorrectos. –

+0

@Konrad, ¿estás seguro de que el tercer punto es incorrecto? – nothrow

+0

Sí ... ver la respuesta de larsmans (pero también mi comentario debajo). –

2

¿Simplemente reconoce una cadena literal para ser algo así como un char *?

Esta parte de la pregunta original no fue respondida tan claramente como me hubiera gustado. Sin embargo, respaldo totalmente (y voté) la respuesta de Yossarian para el resto.

Básicamente, necesita comprender lo que hace el compilador cuando ve una cadena literal en el código.Esa matriz de caracteres (como realmente lo es cualquier cadena estilo c) se almacena en una ubicación completamente diferente a la del código del que forma parte (dependiendo de la arquitectura, los literales numéricos pueden almacenarse en la ubicación misma como parte del ensamblaje/instrucción binaria). Los dos bloques de código aquí son "más o menos" equivalente (ignorar la falta de incluye o declaraciones de espacio de nombres):

int main(void) 
{ 
    cout << "Hello!" << endl; 
    return 0; 
} 

Esto está más cerca de lo que es "realmente" sucede:

const char HELLO_STR[] = { 'H', 'e', 'l', 'l', 'o', '!', 0 }; 

int main(void) 
{ 
    cout << HELLO_STR << endl; 
    return 0; 
} 

Perdóname si Hice un error en array init o lo que sea, pero creo que esto expresa lo que quiero decir en cuanto a dónde se almacena "realmente" el literal de la cadena. No está en línea, pero es una constante invisible a otra parte del programa donde está definido. Además, algunos (¿la mayoría?) Compiladores también arreglan los literales de cadena "juntos" de modo que si tiene el mismo literal usado en 50 lugares, solo almacena uno de ellos, y todos se refieren a la misma constante, guardando memoria

Recuerda que cada vez que utilizas un literal de cadena, estás usando un const char [N] que existe "invisiblemente" en alguna parte, que se convierte implícitamente en const char *.

Cuestiones relacionadas