Question

Considérez les points suivants exemple minimal:

#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;
}

La sortie, à la fois sur g++ et Visual C++, est

hello world
hello world
0x4012a4

La version qui écrit à un objet temporaire, myostream(cout), semble préférer les membres de l'opérateur ostream::operator<<(void *), au lieu de l'opérateur libre operator<<(ostream &, char *).Il semble faire une différence si l'objet a un nom.

Pourquoi est-ce arrivé?Et comment puis-je éviter ce type de comportements?

Modifier:Pourquoi il arrive, c'est maintenant clair à partir des réponses très diverses.Sur la manière d'éviter cela, la suite semble intéressant:

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

Toutefois, cela entraîne toutes sortes d'ambiguïtés.

Était-ce utile?

La solution

rvalues ne peut pas être lié à la non-const de référence.Donc dans votre exemple de la temporaire de type ostream ne peut pas être le premier argument de la gratuité de l'opérateur<<(std::ostream&, const char*) et ce qui est utilisé est le membre de l'opérateur<<(void*).

Si vous en avez besoin, vous pouvez ajouter un appel comme

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

qui va transformer la rvalue dans une référence.

Notez que dans C++0X, l'introduction de référence rvalue permettra d'assurer la surcharge de l'opérateur<< la prise de références rvalue comme paramètre, la résolution de la cause racine du problème.

Autres conseils

Si un objet n'a pas de nom (à-dire qu'elle est temporaire), il ne peut pas être lié à une référence non const. Plus précisément, il ne peut pas être lié au premier paramètre de:

operator<<(ostream &, char *)

Je viens de réaliser partie de la réponse. Le temporaire n'est pas une lvalue, donc il ne peut pas être utilisé comme un argument de type ostream &.

La question "comment puis-je faire ce travail" reste ...

Étant donné qu'aucune des réponses semblent à ce jour pour donner une solution propre, je réglerai la solution sale:

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

Ceci est possible parce que myostream a un constructeur de copie. (Interne, il est soutenu par un std::stringbuf compté ref.)

Alors que 11 C ++ ne résout ce problème car il y a des références rvalue, je pense que cela pourrait être une solution pour les pré-C ++ 11.

La solution est d'avoir une fonction de membre << opérateur où l'on peut jeter une référence non-const à la classe de 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;
        }
};

Eh bien, je ne sais pas le C ++ spec qui provoque, mais il est facile de Suss pourquoi il arrive.

Une vie temporaire sur la pile, le plus souvent à passer à une autre fonction ou d'avoir une seule opération appelée sur elle. Donc, si vous appelez l'opérateur gratuit sur elle:

opérateur << (myostream (Cout))

Il est détruit à la fin de cette opération et le second opérateur « << » pour ajouter la référence endl serait un objet non valide. La valeur de retour de l'opérateur libre « << » serait une référence à un objet temporaire Destructed. La spécification C ++ définit probablement des règles sur les opérateurs libres pour éviter ce scénario de programmeurs C ++ frustrant et confus.

, dans le cas d'un « << (void *) » opérateur membre du temporaire, la valeur de retour est l'objet lui-même, ce qui est encore sur la pile et non détruit, de sorte que le compilateur ne sait pas à détruire ce mais à transmettre à l'opérateur suivant de membre, celui qui tire le endl. Opérateur chaînage est une caractéristique temporaires utile pour le code C ++ succinct, donc je suis sûr que les concepteurs de spécifications C ++ considéré et mis en œuvre le compilateur pour le soutenir intentionnellement.

modifier

Certains ont dit qu'il est de faire une référence non-const. Ce code compile:

#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;
}

Et il retourne

  This works destructing illegal
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top