std :: ostringstream imprimindo o endereço da cadeia em vez do seu conteúdo

Eu tropecei em um comportamento estranho que eu não conseguia explicar a princípio (veja ideone ):

#include  #include  #include  int main() { std::cout << "Reference : " << (void const*)"some data" << "\n"; std::ostringstream s; s << "some data"; std::cout << "Regular Syntax: " << s.str() << "\n"; std::ostringstream s2; std::cout << "Semi inline : " << static_cast(s2 << "some data").str() << "\n"; std::cout << "Inline : " << dynamic_cast( std::ostringstream() << "some data" ).str() << "\n"; } 

Dá a saída:

 Reference : 0x804a03d Regular Syntax: some data Semi inline : some data Inline : 0x804a03d 

Surpreendentemente, no último casting temos o endereço, e não o conteúdo!

Por que ?

A expressão std::ostringstream() cria um temporário, e o operator<< que toma const char* como argumento é uma function livre, mas esta function livre não pode ser chamada em um temporário, pois o tipo do primeiro parâmetro da function é std::ostream& que não pode ser ligado ao object temporário.

Dito isto, < resolve uma chamada para uma function membro que está sobrecarregada para void* que imprime o endereço. Observe que uma function de membro pode ser chamada no temporário.

Para chamar a function free, você precisa converter temporário (que é rvalue) em um lvalue, e aqui está um truque que você pode fazer:

  std::cout << "Inline : " << dynamic_cast( std::ostringstream().flush() << "some data" ).str() << "\n"; 

Isto é, std::ostringstream().flush() retorna std::ostream& que significa, agora a function livre pode ser chamada, passando a referência retornada como primeiro argumento.

Além disso, você não precisa usar o dynamic_cast aqui (que é lento, como é feito em tempo de execução), pois o tipo do object é bastante conhecido e, portanto, você pode usar o static_cast (que é rápido como é feito em tempo de compilation):

  std::cout << "Inline : " << static_cast( std::ostringstream().flush() << "some data" ).str() << "\n"; 

que deve funcionar bem.

Um temporário não pode ligar-se a uma referência ao argumento formal não const.

Portanto, o não-membro << não é pego.

Você obtém a versão void* .

O C ++ 11 corrige isso adicionando uma function de inserção de stream r de valor não membro,

C ++ 11
§27.7.3.9 Inserção de stream de valor
[ostream.rvalue]
template
basic_ostream&
operator<<(basic_ostream&& os, const T& x);

1 Efeitos: os << x
2 Retorna: os

Cheers e hth.

Para começar, a solução mais simples é obter a lista de possíveis sobrecargas que o compilador considerou, por exemplo, tentando isto:

 X x; std::cout << x << "\n"; 

onde X é um tipo sem qualquer sobrecarga para streaming, que produz a seguinte lista de possíveis sobrecargas:

 prog.cpp: In function 'int main()': prog.cpp:21: error: no match for 'operator<<' in 'std::cout << x' include/ostream:112: note: candidates are: std::ostream& std::ostream::operator<<(std::ostream& (*)(std::ostream&)) include/ostream:121: note: std::ostream& std::ostream::operator<<(std::basic_ios<_CharT, _Traits>& (*)(std::basic_ios<_CharT, _Traits>&)) include/ostream:131: note: std::ostream& std::ostream::operator<<(std::ios_base& (*)(std::ios_base&)) include/ostream:169: note: std::ostream& std::ostream::operator<<(long int) include/ostream:173: note: std::ostream& std::ostream::operator<<(long unsigned int) include/ostream:177: note: std::ostream& std::ostream::operator<<(bool) include/bits/ostream.tcc:97: note: std::ostream& std::ostream::operator<<(short int) include/ostream:184: note: std::ostream& std::ostream::operator<<(short unsigned int) include/bits/ostream.tcc:111: note: std::ostream& std::ostream::operator<<(int) include/ostream:195: note: std::ostream& std::ostream::operator<<(unsigned int) include/ostream:204: note: std::ostream& std::ostream::operator<<(long long int) include/ostream:208: note: std::ostream& std::ostream::operator<<(long long unsigned int) include/ostream:213: note: std::ostream& std::ostream::operator<<(double) include/ostream:217: note: std::ostream& std::ostream::operator<<(float) include/ostream:225: note: std::ostream& std::ostream::operator<<(long double) include/ostream:229: note: std::ostream& std::ostream::operator<<(const void*) include/bits/ostream.tcc:125: note: std::ostream& std::ostream::operator<<(std::basic_streambuf<_CharT, _Traits>*) 

Primeiro, digitalizando esta lista, podemos observar que char const* está conspiscuamente ausente e, portanto, é lógico que void const* seja selecionado e, assim, o endereço impresso.

Em uma segunda olhada, notamos que todas as sobrecargas são methods e que nenhuma function livre aparece aqui.

O problema é um problema de binding de referência: como um temporário não pode se ligar a uma referência a não const, sobrecargas do formulário std::ostream& operator<<(std::ostream&,X) são rejeitadas imediatamente e apenas as funções de membro permanecem.

É, no que me diz respeito, um bug de design em C ++, afinal estamos executando uma function de membro mutante em um temporário, e isso requer uma referência (oculta) ao object: x

A solução alternativa, uma vez que você entendeu o que deu errado, é relativamente simples e requer apenas um pequeno wrapper:

 struct Streamliner { template  Streamliner& operator<<(T const& t) { _stream << t; return *this; } std::string str() const { return _stream.str(); } std::ostringstream _stream; }; std::cout << "Inline, take 2: " << (Streamliner() << "some data").str() << "\n"; 

Que imprime o resultado esperado.