Commande de l'opérateur de résolution de surcharge impliquant temporaires
-
20-09-2019 - |
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.
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