Por que a saída do programa abaixo é o que é?
#include <iostream>
using namespace std;
int main(){
cout << "2+3 = " <<
cout << 2 + 3 << endl;
}
produz
2+3 = 15
em vez do esperado
2+3 = 5
Esta questão já passou por vários ciclos de fechamento / reabertura.
Antes de votar para fechar, considere esta meta-discussão sobre esse problema.
;
- e - vírgula no final da primeira linha de saída, não<<
. Você não está imprimindo o que pensa que está imprimindo. Você está fazendocout << cout
, que imprime1
(usacout.operator bool()
, eu acho). Então5
(de2+3
) segue imediatamente, fazendo com que pareça o número quinze.Respostas:
Intencionalmente ou por acidente, você tem
<<
no final da primeira linha de saída, onde você provavelmente quis dizer;
. Então você essencialmente temPortanto, a questão se resume a isso: por que
cout << cout;
imprimir"1"
?Isso acaba sendo, talvez surpreendentemente, sutil.
std::cout
, por meio de sua classe basestd::basic_ios
, fornece um certo operador de conversão de tipo que deve ser usado no contexto booleano, como emEste é um exemplo muito ruim, pois é difícil fazer com que a saída falhe - mas
std::basic_ios
na verdade é uma classe base para os fluxos de entrada e saída e, para entrada, faz muito mais sentido:(sai do loop no final do fluxo ou quando os caracteres do fluxo não formam um número inteiro válido).
Agora, a definição exata desse operador de conversão mudou entre as versões C ++ 03 e C ++ 11 do padrão. Nas versões mais antigas, era
operator void*() const;
(normalmente implementado comoreturn fail() ? NULL : this;
), enquanto nas mais recentes eraexplicit operator bool() const;
(normalmente implementado simplesmente comoreturn !fail();
). Ambas as declarações funcionam bem em um contexto booleano, mas se comportam de maneira diferente quando (mis) usadas fora desse contexto.Em particular, sob as regras do C ++ 03,
cout << cout
seria interpretado comocout << cout.operator void*()
e impresso algum endereço. Sob as regras do C ++ 11,cout << cout
não deve ser compilado, pois o operador é declaradoexplicit
e, portanto, não pode participar de conversões implícitas. Essa foi, de fato, a principal motivação para a mudança - impedindo a compilação de códigos sem sentido. Um compilador que esteja em conformidade com qualquer padrão não produzirá um programa que imprima"1"
.Aparentemente, certas implementações em C ++ permitem misturar e combinar o compilador e a biblioteca de uma maneira que produza resultados não conformes (citando @StephanLechner: "Encontrei uma configuração no xcode que produz 1 e outra que gera um endereço: Language dialect c ++ 98 combinado com "Biblioteca padrão libc ++ (biblioteca padrão LLVM com suporte a c ++ 11)" gera 1, enquanto c ++ 98 combinado com libstdc (biblioteca padrão gnu c ++) gera um endereço; "). Você pode ter um compilador no estilo C ++ 03 que não entenda os
explicit
operadores de conversão (novos no C ++ 11) combinados com uma biblioteca no estilo C ++ 11 que define a conversão comooperator bool()
. Com essa mistura, torna-se possívelcout << cout
interpretar comocout << cout.operator bool()
, que por sua vez é simplescout << true
e imprime"1"
.fonte
Como Igor diz, você obtém isso com uma biblioteca C ++ 11, onde
std::basic_ios
tem ooperator bool
invés deoperator void*
, mas de alguma forma não é declarado (ou tratado como)explicit
. Veja aqui a declaração correta.Por exemplo, um compilador C ++ 11 em conformidade fornecerá o mesmo resultado com
mas no seu caso, isso
static_cast<bool>
está sendo (erroneamente) permitido como uma conversão implícita.Editar: como esse comportamento não é usual ou esperado, pode ser útil conhecer sua plataforma, versão do compilador etc.
Edit 2: Para referência, o código normalmente seria escrito como
ou como
e está misturando os dois estilos que expuseram o bug.
fonte
O motivo da saída inesperada é um erro de digitação. Você provavelmente quis dizer
Se ignorarmos as strings que têm a saída esperada, ficaremos com:
Desde C ++ 11, isso é mal formado.
std::cout
não é implicitamente conversível em algo questd::basic_ostream<char>::operator<<
(ou uma sobrecarga de não-membro) aceitaria. Portanto, um compilador em conformidade com os padrões deve avisá-lo pelo menos para fazer isso. Meu compilador se recusou a compilar seu programa.std::cout
seria conversívelbool
e a sobrecarga booleana do operador de entrada de fluxo teria a saída observada de 1. No entanto, essa sobrecarga é explícita, portanto, não deve permitir uma conversão implícita. Parece que sua implementação de compilador / biblioteca padrão não está estritamente em conformidade com o padrão.Em um padrão anterior ao C ++ 11, isso é bem formado. Naquela época,
std::cout
havia um operador de conversão implícito novoid*
qual havia uma sobrecarga de operador de entrada de fluxo. A saída para isso, no entanto, seria diferente. imprimiria o endereço de memória dostd::cout
objeto.fonte
O código publicado não deve ser compilado para nenhum C ++ 11 (ou compilador compatível posterior), mas deve ser compilado sem sequer um aviso nas implementações anteriores ao C ++ 11.
A diferença é que o C ++ 11 tornou explícita a conversão de um fluxo em um booleano:
operador ostream << é definido com um parâmetro bool. Como uma conversão para bool existia (e não era explícita) é anterior ao C ++ 11,
cout << cout
foi traduzida para acout << true
qual produz 1.E de acordo com C.2.15, isso não deve mais ser compilado a partir do C ++ 11.
fonte
bool
existe no C ++ 03, no entanto, existestd::basic_ios::operator void*()
uma significativa como a expressão de controle de um condicional ou loop.Você pode facilmente depurar seu código dessa maneira. Quando você usa
cout
sua saída é armazenada em buffer para que você possa analisá-la da seguinte maneira:Imagine a primeira ocorrência de
cout
representa o buffer e o operador<<
representa anexando ao final do buffer. O resultado do operador<<
é o fluxo de saída, no seu casocout
. Você começa em:cout << "2+3 = " << cout << 2 + 3 << endl;
Depois de aplicar as regras acima, você obtém um conjunto de ações como este:
buffer.append("2+3 = ").append(cout).append(2 + 3).append(endl);
Como eu disse antes, o resultado
buffer.append()
é buffer. No início, seu buffer está vazio e você tem a seguinte instrução para processar:declaração:
buffer.append("2+3 = ").append(cout).append(2 + 3).append(endl);
amortecedor: empty
Primeiro você tem o
buffer.append("2+3 = ")
que coloca a string fornecida diretamente no buffer e se tornabuffer
. Agora seu estado se parece com isso:declaração:
buffer.append(cout).append(2 + 3).append(endl);
amortecedor: 2+3 =
Depois disso, você continua analisando sua declaração e se depara
cout
com um argumento a ser anexado ao final do buffer. Ocout
é tratado1
assim, você será anexado1
ao final do seu buffer. Agora você está neste estado:declaração:
buffer.append(2 + 3).append(endl);
amortecedor: 2+3 = 1
A próxima coisa que você tem no buffer é que,
2 + 3
como a adição tem maior precedência do que o operador de saída, você primeiro adicionará esses dois números e depois colocará o resultado no buffer. Depois disso você obtém:declaração:
buffer.append(endl);
amortecedor: 2+3 = 15
Finalmente, você adiciona valor
endl
ao final do buffer e possui:declaração:
amortecedor: 2+3 = 15\n
Após esse processo, os caracteres do buffer são impressos no buffer para a saída padrão, um por um. Portanto, o resultado do seu código é
2+3 = 15
. Se você olhar para isso, você obter adicional1
decout
que você tentou imprimir. Ao remover<< cout
da sua declaração, você obterá a saída desejada.fonte
cout << cout
produzir1
em primeiro lugar?" , e você acabou de afirmar que isso ocorre no meio de uma discussão sobre o encadeamento do operador de inserção.