Pergunta

Considere o seguinte exemplo mínimo:

#include <iostream>

using namespace std;

class myostream : public ostream {
    public:
        myostream(ostream const &other) :
            ostream(other.rdbuf())
        { }
};

int main() {
    cout << "hello world" << endl;

    myostream s(cout);
    s << "hello world" << endl;

    myostream(cout) << "hello world" << endl;
}

A saída, tanto no g++ quanto no Visual C++, é

hello world
hello world
0x4012a4

A versão que grava em um objeto temporário, myostream(cout), parece preferir o operador membro ostream::operator<<(void *), em vez do operador livre operator<<(ostream &, char *).Parece fazer diferença se o objeto tem ou não um nome.

Por que isso acontece?E como posso evitar esse comportamento?

Editar:Por que isso acontece agora fica claro a partir de várias respostas.Quanto a como evitar isso, o seguinte parece atraente:

class myostream : public ostream {
    public:
        // ...
        myostream &operator<<(char const *str) {
            std::operator<<(*this, str);
            return *this;
        }
};

No entanto, isso resulta em todos os tipos de ambigüidades.

Foi útil?

Solução

rvalues ​​não podem ser vinculados a referências não const.Portanto, no seu exemplo, o temporário do tipo ostream não pode ser o primeiro argumento do operador livre<<(std::ostream&, char const*) e o que é usado é o operador membro<<(void*).

Se precisar, você pode adicionar uma chamada como

myostream(cout).flush() << "foo";

que transformará o rvalue em uma referência.

Observe que em C++ 0X, a introdução da referência de rvalue permitirá fornecer sobrecarga de operador<< tomando referências de rvalue como parâmetro, resolvendo a causa raiz do problema.

Outras dicas

Se um objeto não tiver um nome (ou seja, é temporário), ele não pode ser vinculado a uma referência não consagrada. Especificamente, não pode ser vinculado ao primeiro parâmetro de:

operator<<(ostream &, char *)

Acabei de perceber papel da resposta. O temporário não é um lvalue, portanto não pode ser usado como um argumento do tipo ostream &.

A pergunta "Como posso fazer isso funcionar" permanece ...

Como nenhuma das respostas até agora parece dar uma solução limpa, vou me contentar com a solução suja:

myostream operator<<(myostream stream, char const *str) {
    std::operator<<(stream, str);
    return stream;
}

Isso só é possível porque myostream tem um construtor de cópia. (Internamente, é apoiado por um refinado std::stringbuf.)

Embora o C ++ 11 resolva esse problema, pois existem referências de RValue, acho que isso pode ser uma solução alternativa para o pré-C ++ 11.

A solução é ter uma função de membro << Operador onde podemos ser lançados em uma referência não consagrada à classe base:

class myostream : public ostream {
    public:
        // ...
        template<typename T>
        ostream &operator<<(const T &t) {
            //now the first operand is no longer a temporary,
            //so the non-member operators will overload correctly
            return static_cast<ostream &>(*this) << t;
        }
};

Bem, não conheço a especificação C ++ que causa isso, mas é fácil divulgar por que isso acontece.

Uma vida temporária na pilha, geralmente para ser passada para outra função ou ter uma única operação chamada nela. Então, se você ligar para o operador gratuito nele:

Operador << (Myostream (Cout))

Ele é destruído no final desta operação e o segundo operador "<<" para anexar o ENDL referenciaria um objeto inválido. O valor de retorno do operador gratuito "<<" seria uma referência a um objeto temporário destruído. A especificação C ++ provavelmente define regras sobre os operadores gratuitos para impedir que esse cenário frustre e confunda programadores C ++.

Agora, no caso de um operador de membro "<< (void*)" no temporário, o valor de retorno é o próprio objeto, que ainda está na pilha e não destruído, então o compilador sabe não destruí -lo, mas passar para o próximo operador de membro, aquele que leva o final. O encadeamento do operador em temporários é um recurso útil para o código C ++ sucinto, por isso tenho certeza de que os designers de especificações C ++ o consideraram e implementaram o compilador para apoiá -lo intencionalmente.

editar

Alguns disseram que tem a ver com uma referência não consagrada. Este código compila:

#include <iostream>
using namespace std;
class myostream : public ostream { 
    public: 
        myostream(ostream const &other) : 
            ostream(other.rdbuf()) 
        { } 
            ~myostream() { cout << " destructing "; }
    }; 
int _tmain(int argc, _TCHAR* argv[])
{
    basic_ostream<char>& result = std::operator << (myostream(cout), "This works");
    std::operator << (result, "illegal");
         return 0;
}

E retorna

  This works destructing illegal
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top