Der Operator << muss genau ein Argument annehmen


91

Ah

#include "logic.h"
...

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

logik.cpp

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

Wenn ich kompiliere, heißt es:

std :: ostream & Logik :: Operator << (std :: ostream &, A &) 'muss genau ein Argument annehmen.

Was ist das Problem?

Antworten:


126

Das Problem ist, dass Sie es innerhalb der Klasse definieren, die

a) bedeutet, dass das zweite Argument implizit ist ( this) und

b) es wird nicht tun, was Sie wollen, nämlich verlängern std::ostream.

Sie müssen es als freie Funktion definieren:

class A { /* ... */ };
std::ostream& operator<<(std::ostream&, const A& a);

8
Außerdem deklariert er es als Freundfunktion und definiert es als Mitgliedsfunktion.
Asaelr

Wie unter en.cppreference.com/w/cpp/language/operators erwähnt , werden "die Überladungen von Operator >> und Operator <<, die einen std :: istream & oder std :: ostream & als linkes Argument annehmen, als Einfügung und bezeichnet Extraktionsoperatoren. Da sie den benutzerdefinierten Typ als das richtige Argument verwenden (b in a @ b), müssen sie als Nichtmitglieder implementiert werden. "
Morteza

49

Eine Friend-Funktion ist keine Member-Funktion. Das Problem besteht also darin, dass Sie sich operator<<als Freund von deklarieren A:

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

Versuchen Sie dann, es als Elementfunktion der Klasse zu definieren logic

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

Sind Sie verwirrt darüber, ob logices sich um eine Klasse oder einen Namespace handelt?

Der Fehler liegt darin, dass Sie versucht haben, ein Mitglied operator<<mit zwei Argumenten zu definieren. Dies bedeutet, dass drei Argumente einschließlich des impliziten thisParameters verwendet werden. Der Operator kann nur zwei Argumente annehmen, sodass beim Schreiben a << bdie beiden Argumente aund sind b.

Sie möchten ostream& operator<<(ostream&, const A&)als Nichtmitgliedsfunktion definieren, definitiv nicht als Mitglied von, logicda dies nichts mit dieser Klasse zu tun hat!

std::ostream& operator<<(std::ostream& os, const A& a)
{
  return os << a.number;
}

3

Ich bin auf dieses Problem mit Vorlagenklassen gestoßen. Hier ist eine allgemeinere Lösung, die ich verwenden musste:

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

Jetzt: * Meine Funktion toString () kann nicht inline sein, wenn sie in cpp versteckt wird. * Du hast einen Code in der Kopfzeile, ich konnte ihn nicht loswerden. * Der Operator ruft die toString () -Methode auf, sie ist nicht inline.

Der Hauptteil des Operators << kann in der friend-Klausel oder außerhalb der Klasse deklariert werden. Beide Optionen sind hässlich. :(

Vielleicht verstehe ich etwas falsch oder vermisse etwas, aber nur das Vorwärtsdeklarieren der Operatorvorlage führt nicht zu einer Verknüpfung mit gcc.

Das funktioniert auch:

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

Ich denke, Sie können auch die Vorlagenprobleme vermeiden, die Deklarationen in Headern erzwingen, wenn Sie eine übergeordnete Klasse verwenden, die nicht als Vorlage für die Implementierung des Operators << vorgesehen ist, und eine virtuelle toString () -Methode verwenden.


0

Wenn Sie operator<<als Elementfunktion definieren, hat diese eine andere zerlegte Syntax als wenn Sie ein Nichtmitglied verwendet haben operator<<. Ein Nichtmitglied operator<<ist ein binärer Operator, wobei ein Mitglied operator<<ein unärer Operator ist.

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

Also ... wie nennt man sie wirklich? Operatoren sind in gewisser Hinsicht seltsam. Ich fordere Sie auf, die operator<<(...)Syntax in Ihren Kopf zu schreiben , damit die Dinge Sinn ergeben.

MyObj mo;

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

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

Oder Sie könnten versuchen, den Nicht-Mitglied-Binäroperator aufzurufen:

MyObj mo;

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

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

Sie sind nicht verpflichtet, diese Operatoren dazu zu bringen, sich intuitiv zu verhalten, wenn Sie sie zu Elementfunktionen machen. Sie können festlegen, dass eine Elementvariable operator<<(int)nach links verschoben wird, wenn Sie möchten schreiben.

Fast schließlich kann es Zeiten geben, in denen beide Zerlegungen für einen Operatoranruf gültig sind. Hier können Probleme auftreten, und wir werden dieses Gespräch verschieben.

Beachten Sie zum Schluss, wie seltsam es sein kann, einen unären Member-Operator zu schreiben, der wie ein binärer Operator aussehen soll (da Sie Member-Operatoren virtuell machen können ..... auch versuchen, diesen Pfad nicht weiterzuentwickeln und zu durchlaufen .... )

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

    int value = 8;
};

Diese Syntax wird jetzt viele Programmierer irritieren ...

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");

Beachten Sie, wie das coutzweite Argument in der Kette hier ist .... ungerade richtig?

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.