operator << musi przyjmować dokładnie jeden argument

94

ah

#include "logic.h"
...

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

logic.cpp

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

Kiedy kompiluję, mówi:

std :: ostream & logic :: operator << (std :: ostream &, A &) 'musi przyjmować dokładnie jeden argument.

Jaki jest problem?

Jak As
źródło

Odpowiedzi:

132

Problem polega na tym, że definiujesz to w klasie, która

a) oznacza, że ​​drugi argument jest niejawny ( this) i

b) nie zrobi tego, co chcesz, a mianowicie przedłuży std::ostream.

Musisz zdefiniować to jako darmową funkcję:

class A { /* ... */ };
std::ostream& operator<<(std::ostream&, const A& a);
Cat Plus Plus
źródło
9
Ponadto deklaruje ją jako funkcję zaprzyjaźnioną i definiuje ją jako funkcję składową.
asaelr
Jak wspomniano na stronie en.cppreference.com/w/cpp/language/operators , „przeciążenia operatora >> i operatora <<, które przyjmują std :: istream & lub std :: ostream & jako argument z lewej strony, są znane jako wstawianie i operatory ekstrakcji. Ponieważ jako właściwy argument przyjmują typ zdefiniowany przez użytkownika (b w a @ b), muszą być zaimplementowane jako nie-składowe ".
Morteza
49

Funkcja znajomego nie jest funkcją operator<<składową , więc problem polega na tym, że deklarujesz jako znajomego A:

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

następnie spróbuj zdefiniować ją jako funkcję składową klasy logic

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

Czy nie wiesz, czy logicjest to klasa, czy przestrzeń nazw?

Błąd wynika z tego, że próbowałeś zdefiniować element członkowski operator<<przyjmujący dwa argumenty, co oznacza, że ​​przyjmuje trzy argumenty, w tym niejawny thisparametr. Operator może przyjąć tylko dwa argumenty, więc podczas pisania a << bdwoma argumentami są ai b.

Chcesz zdefiniować ostream& operator<<(ostream&, const A&)jako funkcję niebędącą członkiem, na pewno nie jako element członkowski, logicponieważ nie ma ona nic wspólnego z tą klasą!

std::ostream& operator<<(std::ostream& os, const A& a)
{
  return os << a.number;
}
Jonathan Wakely
źródło
3

Napotkałem ten problem w przypadku klas opartych na szablonach. Oto bardziej ogólne rozwiązanie, którego musiałem użyć:

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

Teraz: * Moja funkcja toString () nie może być wbudowana, jeśli ma być schowana w cpp. * Utknąłeś z jakimś kodem w nagłówku, nie mogłem się go pozbyć. * Operator wywoła metodę toString (), nie jest ona wstawiona.

Treść operatora << można zadeklarować w klauzuli friend lub poza klasą. Obie opcje są brzydkie. :(

Może nie rozumiem czegoś lub czegoś mi brakuje, ale samo zadeklarowanie szablonu operatora nie łączy się w gcc.

To też działa:

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

Myślę, że można również uniknąć problemów z tworzeniem szablonów wymuszających deklaracje w nagłówkach, jeśli używasz klasy nadrzędnej, która nie jest szablonem do implementacji operatora <<, i używasz wirtualnej metody toString ().

Dan Truong
źródło
0

Jeśli zdefiniujesz operator<<jako funkcję składową, będzie ona miała inną zdekomponowaną składnię niż w przypadku użycia funkcji niebędącej składową operator<<. Element niebędący członkiem operator<<jest operatorem binarnym, gdzie element członkowski operator<<jest operatorem jednoargumentowym.

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

Więc… jak naprawdę je nazywasz? Operatory są pod pewnymi względami dziwne, wezwam cię do napisania operator<<(...)składni w twojej głowie, aby wszystko miało sens.

MyObj mo;

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

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

Lub możesz spróbować wywołać operator binarny niebędący składnikiem:

MyObj mo;

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

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

Nie masz obowiązku zmuszać tych operatorów do zachowywania się intuicyjnie, gdy przekształcasz je w funkcje składowe, możesz zdefiniować operator<<(int)przesunięcie w lewo jakiejś zmiennej składowej, jeśli chcesz, zrozum, że ludzie mogą być nieco zaskoczeni, bez względu na to, ile komentarzy możesz pisać.

Prawie na koniec mogą zdarzyć się sytuacje, w których obie dekompozycje dla połączenia operatora są prawidłowe, możesz mieć tutaj kłopoty, a my odłożymy tę rozmowę.

Na koniec zwróć uwagę, jak dziwne może być napisanie jednoargumentowego operatora składowego, który ma wyglądać jak operator binarny (ponieważ możesz sprawić, że operatorzy elementów członkowskich będą wirtualnymi ..... również próbując nie zdecentralizować i nie spuścić tej ścieżki ... )

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

    int value = 8;
};

Ta składnia będzie teraz irytować wielu programistów ...

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

Zwróć uwagę, dlaczego tutaj coutjest drugi argument w łańcuchu… dziwne, prawda?

Rinzler
źródło