web-dev-qa-db-fra.com

Comment fonctionne réellement <<

Je me demandais comment std::cout est capable d'utiliser << comme il le fait.

Ma principale perplexité est de savoir si std::cout comme instance de quelque chose. Fondamentalement, comment << défini? Si je fais cela pour une classe personnalisée, j'ai besoin d'une instance quelconque ...

Je pourrais voir l'implémentation comme une sorte de hack avec des pointeurs vides ou quelque chose, mais j'aimerais voir la façon dont c'est fait.

Est-ce que quelqu'un ici le sait? Merci

47
Ori

std::cout est une instance de std::ostream. std::cout << "something" appelle l'un des operator<< surcharges comme ce serait le cas pour toute instance de std::ostream.

Il est "spécial" en ce qu'il fait référence à la console, mais sinon il se comporte exactement comme le ferait un ofstream ou un ostringstream.

EDIT : Le chaînage fonctionne comme il fonctionne pour tous les autres opérateurs. Considérer:

class MyType
{
    friend std::ostream& operator<<(std::ostream& target, const MyType& source);
    int val;
public:
    MyType()
        : val(0)
    { }
    MyType& Add(int toAdd)
    {
        val += toAdd;
        return *this;
    }
};

MyType& operator+(MyType& target, int toAdd)
{
    return target.Add(toAdd);
}

std::ostream& operator<<(std::ostream& target, const MyType& source)
{
    target << source.val;
    return target; //Make chaining work
}

int main()
{
    MyType value1;
    value1 + 2 + 3 + 4;
    std::cout << value1 << " and done!" << std::endl;
}

Dans ce cas, le chaînage de + s sur MyType fonctionne pour la même raison que <<s fonctionne en plus de std::ostream. Tous les deux + et << sont associatifs à gauche, ce qui signifie qu'ils sont évalués de gauche à droite. Dans le cas d'opérateurs surchargés, l'opérateur est remplacé par l'appel de fonction équivalent.

EDIT2: Plus en détail:

Disons que vous êtes le compilateur et que vous analysez

std::cout << value1 << " and done!" << std::endl;

Première, << est associatif à gauche, vous commencez donc à gauche. Vous évaluez le premier << et le transformer en appel de fonction:

operator<<(std::cout, value1) << " and done!" << std::endl;

Vous voyez alors que vous avez à nouveau un std::ostream (résultat de l'appel à operator<<) et un char *, que vous transformez à nouveau en appel de fonction:

operator<<(operator<<(std::cout, value1)," and done!") << std::endl;

et ainsi de suite jusqu'à ce que vous ayez traité l'intégralité de la déclaration.

47
Billy ONeal