2010-07-12 18 views
5

El siguiente segmento demuestra mi problema: (error de compilación en GCC)pregunta acerca del uso de string :: intercambio() con los temporales

stringstream ss; 
string s; 
ss << "Hello"; 

// This fails: 
// s.swap(ss.str()); 

// This works: 
ss.str().swap(s); 

Mi error:

constSwap.cc:14: error: no matching function for call to 'std::basic_string<char, std::char_traits<char>, std::allocator<char> >::swap(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)' 
basic_string.tcc:496: note: candidates are: void std::basic_string<_CharT, _Traits, _Alloc>::swap(std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>] 

Si bien entiendo que str () en stringstream devuelve un temporal, no tiene sentido y no fue inmediatamente evidente que debería haber estado llamando el intercambio en el temporal con la variable local como parámetro en lugar de mi primer instinto.

Obviamente, la asignación directa funciona mejor, y los estándares más nuevos de C++ tienen una semántica de movimiento que es perfecta, pero estos no están disponibles para mi implementación.

Visual Studio no proporciona este problema debido a que se relaja con respecto al estándar de C++. Ya entiendo un poco la referencia completa de una cosa temporal (que supongo que es la razón de mis errores de compilación).

Mi pregunta es: ¿Alguien me puede explicar si esta es la única solución, y tal vez me explique cómo pensar en esto en el futuro para poder detectar y solucionar problemas similares?

(Si nadie tiene ningún grandes ideas que estoy al menos publicar esto aquí para las personas con problemas similares)

+0

Otra nota, para que un intercambio funcione, uno tiene que pasar en una referencia no constante. Y todo lo que tengo es (dado que es un retorno temporal) un valor constante. Así que esto lo entiendo ... simplemente no tiene sentido lógico inmediato que la otra opción sea legal (aunque entiendo por qué eso también es así). – Marius

+1

¿Está compilando el código en VS2010? Tiene 2 variantes de swap. Uno toma referencia y la otra referencia rvalue. Esa podría ser la razón por la cual está compilando. – Jagannath

+0

@Jagannath: Estoy usando VS2005 en este momento, así que, lamentablemente, ninguna de estas técnicas más nuevas está disponible para mí. – Marius

Respuesta

2

Después de haber utilizado el canje-con-temporales modismo suficientes veces, con líneas como

std::vector<int>().swap(v); // clear and minimize capacity 

o

std::vector<int>(v).swap(v); // shrink to fit 

esto no parece tan fuera de lugar. Es normal para llamar a swap como una función miembro de un objeto temporal. Por supuesto, no es tan idiomático usar swap para completar una cadena construida por defecto en lugar de usar un constructor de copia, como ya se mencionó.

+0

Esto mejor respondió mi pregunta. Creo que debería acostumbrarme al idioma. – Marius

7

No se puede obligar a un temporal a una referencia no const. Por esta razón, el temporal devuelto desde ss.str() no se puede pasar al std::string::swap que espera modificar su parámetro (por lo tanto, toma su argumento usando non-const&).

La segunda versión funciona como está llamando a una función miembro en el objeto temporal que está permitido.

¿Pero por qué quieres cambiar en primer lugar? Normalmente, un simple:

std::string s(ss.str()); 

debería ser lo suficientemente bueno. Esto no es menos eficiente que el intercambio (al menos en C++ 0x con semántica de movimiento), pero al mismo tiempo es mucho más legible.

+0

+1 para escribir más rápido que yo. – Cogwheel

+0

'std :: string s (s.str())' es más lento que el swap, porque tiene que copiar la cadena (excepto en C++ 0x que tiene semántica de movimiento usando las referencias '&&'). –

+0

Tiene toda la razón, edité mi respuesta en consecuencia. – hkaiser

1

El motivo por el que no se puede pasar el argumento temporal como swap es que el argumento se pasa por referencia no constante. Y los temporales solo pueden estar limitados por const-references. Esto se extiende sobre §8.5.3, con el ser apropiada en el párrafo 5, segundo punto:

§8.5.3 A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

  • [bullet one, does not apply here: binding to non-const ref ]

  • Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const).

La razón por la escritura de la llamada en la dirección opuesta funciona es que la norma permite llamar a mutar funciones miembro de temporal objetos.

§3.10/10 An lvalue for an object is necessary in order to modify the object except that an rvalue of class type can also be used to modify its referent under certain circumstances. [Example: a member function called for an object (9.3) can modify the object. ]

La línea de razonamiento que se solicita para el futuro es que, mientras que se puede modificar un temporal a través de él de las propias funciones, no se puede pasar a una función o un método que podría modificarlo (pasan por no constante referencia)

Cuestiones relacionadas