l'opérateur << doit prendre exactement un argument

91

ah

#include "logic.h"
...

class A
{
friend ostream& operator<<(ostream&, A&);
...
};

logic.cpp

#include "a.h"
...
ostream& logic::operator<<(ostream& os, A& a)
{
...
}
...

Quand je compile, il dit:

std :: ostream & logic :: operator << (std :: ostream &, A &) 'doit prendre exactement un argument.

Quel est le problème?

En tant que
la source

Réponses:

127

Le problème est que vous le définissez dans la classe, ce qui

a) signifie que le deuxième argument est implicite ( this) et

b) il ne fera pas ce que vous voulez qu'il fasse, à savoir prolonger std::ostream.

Vous devez le définir comme une fonction libre:

class A { /* ... */ };
std::ostream& operator<<(std::ostream&, const A& a);
Cat Plus Plus
la source
8
De plus, il la déclare comme une fonction amie et la définit comme une fonction membre.
asaelr
Comme mentionné sur en.cppreference.com/w/cpp/language/operators , "les surcharges d'opérateur >> et d'opérateur << qui prennent un std :: istream & ou std :: ostream & comme argument de gauche sont appelées insertion et opérateurs d'extraction. Comme ils prennent le type défini par l'utilisateur comme le bon argument (b dans a @ b), ils doivent être implémentés en tant que non-membres ".
Morteza le
49

Une fonction ami n'est pas une fonction membre, donc le problème est que vous déclarez operator<<comme ami de A:

 friend ostream& operator<<(ostream&, A&);

puis essayez de le définir comme une fonction membre de la classe logic

 ostream& logic::operator<<(ostream& os, A& a)
          ^^^^^^^

Êtes-vous confus quant à savoir s'il logics'agit d'une classe ou d'un espace de noms?

L'erreur est due au fait que vous avez essayé de définir un membre en operator<<prenant deux arguments, ce qui signifie qu'il prend trois arguments, y compris le thisparamètre implicite . L'opérateur ne peut prendre que deux arguments, de sorte que lorsque vous écrivez a << bles deux arguments sont aet b.

Vous voulez définir ostream& operator<<(ostream&, const A&)comme une fonction non- membre, certainement pas comme membre de logiccar cela n'a rien à voir avec cette classe!

std::ostream& operator<<(std::ostream& os, const A& a)
{
  return os << a.number;
}
Jonathan Wakely
la source
3

J'ai rencontré ce problème avec des classes basées sur des modèles. Voici une solution plus générale que j'ai dû utiliser:

template class <T>
class myClass
{
    int myField;

    // Helper function accessing my fields
    void toString(std::ostream&) const;

    // Friend means operator<< can use private variables
    // It needs to be declared as a template, but T is taken
    template <class U>
    friend std::ostream& operator<<(std::ostream&, const myClass<U> &);
}

// Operator is a non-member and global, so it's not myClass<U>::operator<<()
// Because of how C++ implements templates the function must be
// fully declared in the header for the linker to resolve it :(
template <class U>
std::ostream& operator<<(std::ostream& os, const myClass<U> & obj)
{
  obj.toString(os);
  return os;
}

Maintenant: * Ma fonction toString () ne peut pas être en ligne si elle va être cachée dans cpp. * Vous êtes coincé avec du code dans l'en-tête, je n'ai pas pu m'en débarrasser. * L'opérateur appellera la méthode toString (), elle n'est pas en ligne.

Le corps de l'opérateur << peut être déclaré dans la clause friend ou en dehors de la classe. Les deux options sont laides. :(

Peut-être que je ne comprends pas bien ou que je manque quelque chose, mais simplement déclarer en avant le modèle d'opérateur ne lie pas dans gcc.

Cela fonctionne aussi:

template class <T>
class myClass
{
    int myField;

    // Helper function accessing my fields
    void toString(std::ostream&) const;

    // For some reason this requires using T, and not U as above
    friend std::ostream& operator<<(std::ostream&, const myClass<T> &)
    {
        obj.toString(os);
        return os;
    }
}

Je pense que vous pouvez également éviter les problèmes de création de modèles qui forcent les déclarations dans les en-têtes, si vous utilisez une classe parente qui n'est pas basée sur un modèle pour implémenter l'opérateur <<, et utilisez une méthode virtuelle toString ().

Dan Truong
la source
0

Si vous définissez en operator<<tant que fonction membre, elle aura une syntaxe décomposée différente de celle si vous utilisiez un non-membre operator<<. Un non-membre operator<<est un opérateur binaire, où un membre operator<<est un opérateur unaire.

// Declarations
struct MyObj;
std::ostream& operator<<(std::ostream& os, const MyObj& myObj);

struct MyObj
{
    // This is a member unary-operator, hence one argument
    MyObj& operator<<(std::ostream& os) { os << *this; return *this; }

    int value = 8;
};

// This is a non-member binary-operator, 2 arguments
std::ostream& operator<<(std::ostream& os, const MyObj& myObj)
{
    return os << myObj.value;
}

Alors ... comment les appelez-vous vraiment? Les opérateurs sont étranges à certains égards, je vous mets au défi d'écrire la operator<<(...)syntaxe dans votre tête pour donner un sens aux choses.

MyObj mo;

// Calling the unary operator
mo << std::cout;

// which decomposes to...
mo.operator<<(std::cout);

Ou vous pouvez essayer d'appeler l'opérateur binaire non membre:

MyObj mo;

// Calling the binary operator
std::cout << mo;

// which decomposes to...
operator<<(std::cout, mo);

Vous n'avez aucune obligation de faire en sorte que ces opérateurs se comportent de manière intuitive lorsque vous les transformez en fonctions membres, vous pouvez définir operator<<(int)pour déplacer à gauche une variable membre si vous le souhaitez, comprenez que les gens peuvent être un peu pris au dépourvu, quel que soit le nombre de commentaires que vous pouvez écrire.

Presque enfin, il peut y avoir des moments où les deux décompositions pour un appel d'opérateur sont valides, vous pouvez avoir des problèmes ici et nous différerons cette conversation.

Enfin, notez à quel point il peut être étrange d'écrire un opérateur membre unaire censé ressembler à un opérateur binaire (car vous pouvez rendre les opérateurs membres virtuels ..... en essayant également de ne pas dévoluer et de suivre ce chemin .... )

struct MyObj
{
    // Note that we now return the ostream
    std::ostream& operator<<(std::ostream& os) { os << *this; return os; }

    int value = 8;
};

Cette syntaxe va maintenant irriter de nombreux codeurs ...

MyObj mo;

mo << std::cout << "Words words words";

// this decomposes to...
mo.operator<<(std::cout) << "Words words words";

// ... or even further ...
operator<<(mo.operator<<(std::cout), "Words words words");

Remarquez comment le coutdeuxième argument de la chaîne est ici ... étrange, non?

Rinzler
la source