оператор << должен принимать ровно один аргумент

94

ах

#include "logic.h"
...

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

logic.cpp

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

Когда я компилирую, он говорит:

std :: ostream & logic :: operator << (std :: ostream &, A &) 'должен принимать ровно один аргумент.

В чем проблема?

Как Как
источник

Ответы:

132

Проблема в том, что вы определяете его внутри класса, который

a) означает, что второй аргумент является неявным ( this) и

б) он не будет делать того, что вы хотите, а именно расширять std::ostream.

Вы должны определить его как бесплатную функцию:

class A { /* ... */ };
std::ostream& operator<<(std::ostream&, const A& a);
Кошка Плюс Плюс
источник
9
Кроме того, он объявляет ее как функцию друга и определяет ее как функцию-член.
asaelr
Как упоминалось на en.cppreference.com/w/cpp/language/operators , «перегрузки оператора >> и operator <<, которые принимают std :: istream & или std :: ostream & в качестве аргумента слева, известны как вставка и операторы извлечения. Поскольку они принимают определяемый пользователем тип в качестве правильного аргумента (b в a @ b), они должны быть реализованы как не-члены ".
Мортеза,
49

Функция друга не является функцией-членом, поэтому проблема в том, что вы объявляете operator<<ее другом A:

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

затем попробуйте определить его как функцию-член класса logic

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

Вы не уверены, logicчто это класс или пространство имен?

Ошибка связана с тем, что вы пытались определить член, operator<<принимающий два аргумента, что означает, что он принимает три аргумента, включая неявный thisпараметр. Оператор может принимать только два аргумента, поэтому при записи a << bдвумя аргументами будут aи b.

Вы хотите определить ostream& operator<<(ostream&, const A&)функцию, не являющуюся членом, а определенно не членом, logicпоскольку она не имеет ничего общего с этим классом!

std::ostream& operator<<(std::ostream& os, const A& a)
{
  return os << a.number;
}
Джонатан Уэйкли
источник
3

Я столкнулся с этой проблемой с шаблонными классами. Вот более общее решение, которое мне пришлось использовать:

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

Теперь: * Моя функция toString () не может быть встроенной, если она будет спрятана в cpp. * Вы застряли с каким-то кодом в заголовке, я не смог от него избавиться. * Оператор вызовет метод toString (), он не встроен.

Тело оператора << может быть объявлено в предложении друга или вне класса. Оба варианта уродливы. :(

Возможно, я что-то не понимаю или что-то упускаю, но простое прямое объявление шаблона оператора не ссылается на gcc.

Это тоже работает:

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

Я думаю, вы также можете избежать проблем с шаблонами, вызывающих объявления в заголовках, если вы используете родительский класс, который не имеет шаблона для реализации operator <<, и используете виртуальный метод toString ().

Дэн Чыонг
источник
0

Если вы определяете operator<<функцию-член, она будет иметь другой разложенный синтаксис, чем если бы вы использовали не-член operator<<. Не-член operator<<- это бинарный оператор, а член operator<<- унарный оператор.

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

Итак .... как вы их на самом деле называете? Операторы в некотором роде странные, я предлагаю вам написать operator<<(...)синтаксис в уме, чтобы все было понятно.

MyObj mo;

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

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

Или вы можете попытаться вызвать бинарный оператор, не являющийся членом:

MyObj mo;

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

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

Вы не обязаны заставлять эти операторы вести себя интуитивно, когда вы превращаете их в функции-члены, вы можете определить operator<<(int)сдвиг влево для некоторой переменной-члена, если хотите, понимая, что люди могут быть немного застигнуты врасплох, независимо от того, сколько комментариев вы можете написать.

Почти наконец, могут быть моменты, когда оба разложения для вызова оператора действительны, у вас могут возникнуть проблемы, и мы отложим этот разговор.

Наконец, обратите внимание, как странно было бы написать унарный оператор-член, который должен выглядеть как бинарный оператор (поскольку вы можете сделать операторы-члены виртуальными ... также пытаясь не переходить и не спускаться по этому пути ... )

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

    int value = 8;
};

Этот синтаксис сейчас будет раздражать многих программистов ....

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

Обратите внимание, что здесь coutвторой аргумент в цепочке .... странно, правда?

Ринзлер
источник