оператор << повинен приймати рівно один аргумент


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

Проблема в тому, що ви визначаєте це всередині класу, який

а) означає, що другий аргумент є неявним ( this) та

б) він не буде робити те, що ти хочеш, а саме продовжувати std::ostream.

Ви повинні визначити це як безкоштовну функцію:

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

9
Крім того, він оголошує це як функцію друга і визначає як функцію-член.
asaelr

Як згадувалося на en.cppreference.com/w/cpp/language/operators , "перевантаження оператора >> та оператора <<, які приймають 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 (), він не вбудований.

Тіло оператора << може бути оголошено в реченні friend або поза класом. Обидва варіанти некрасиві. :(

Можливо, я щось не розумію або щось пропускаю, але просто попереднє оголошення шаблону оператора не посилається на 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;
    }
}

Я думаю, ви також можете уникнути проблем із шаблонуванням примусових оголошень у заголовках, якщо ви використовуєте батьківський клас, який не шаблонований для реалізації оператора <<, і використовуєте віртуальний метод 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тут другий аргумент у ланцюжку .... дивно, правда?

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.