нет оператора << найдено, который принимает правый операнд типа класса Foo, несмотря на то, что один определен - PullRequest
0 голосов
/ 28 апреля 2018

[решено] У меня было немного хвастовства, которое было неточным, проблема связана с компиляцией.

Мой код:

#include <iostream>
using namespace std;

struct Circle {
    void write(ostream& os) const {
        os<<*this;
    }
};

ostream& operator<<(ostream& os, const Circle& rhs_c) {
    os<<"circle";
    return os;
}

int main() {
    Circle c1;
    c1.write(cout);
    return 0;
}

Я получаю следующую ошибку:

C2679 binary'<<': no operator found which takes a right-hand operand of type 'Circle' (or there is no acceptible conversion)

Принимая во внимание, что если глобальный оператор << на самом деле имеет то, что не соответствует операндам, которые я получаю: </p>

no operator "<<" matches these operands

У меня есть причины иметь такую ​​искаженную функцию печати, которая может сломаться, зная, почему верхний код неверен.

Ответы [ 2 ]

0 голосов
/ 28 апреля 2018

Проблема в том, что вы пытаетесь вызвать оператора, который "еще не существует". Поскольку вы определили свою функцию write () в заголовке и объявили оператор << ниже класса, в точке вызова, что касается компилятора, оператор << не включен в набор перегрузки. </p>

Чтобы включить его в набор перегрузки, вы должны сначала объявить функцию перед вашим классом. Но чтобы объявить функцию, она должна знать о классе, поэтому вы также должны объявить это. Это грязно и звучит хуже, чем это:

// declare that Circle is a class, so the compiler will know what a reference to it means
class Circle;

// declare the operator, but not define it yet, since Circle is incomplete.    
ostream& operator<<(ostream& os, const Circle& rhs_c);

class Circle {
    void write(ostream& os) const {
        os<<*this;
    }
};

inline ostream& operator<<(ostream& os, const Circle& rhs_c) {
    os<<"circle";
}

Таким образом, когда оператор вызывается в write (), компилятор узнает, что оператор существует, а затем он будет определен и компоновщик выполнит вызов.

Существует и другой способ - НЕ реализовывать write () в определении класса, но после объявления оператора <<. Возможно, переместите определение в файл .cpp, но вы также можете реализовать его вне класса для того же эффекта: </p>

class Circle {
    void write(ostream& os) const;
};

inline ostream& operator<<(ostream& os, const Circle& rhs_c) {
    os <<"circle";
}

inline void Circle::write(ostream& os) const {
    return os << *this;
}

В обоих случаях используется одно и то же: определение write () физически располагается после объявления оператора, который он ожидает использовать. (Если он помещен в файл .cpp, удалите inline .)

Лично я рекомендую переместить реализацию в файл .cpp. Вы не только избегаете подобных проблем, вы также можете избежать включения в свой заголовок, и это большой файл (десятки тысяч строк кода для компиляции каждый раз, когда ваш заголовок включен.) Вместо этого вы должны использовать только «вперед» объявления к классам iostream доступны из заголовка, и тогда вы можете обойтись без этого шаблона:

// header file
#include <iosfwd>
#pragma once

class Circle {
   void write(std::ostream&) const;
};

std::ostream& operator<<(std::ostream& os, Circle const& circle);

А потом

// .cpp file
#include "Circle.hpp"
#include <iostream>

void Circle::write(std::ostream& os) {
   os << *this;
}

std::ostream& operator<<(std::ostream& os, Circle const& circle) {
   return os << "circle";
}

Разделив его таким образом, вы включаете намного меньший класс в свой заголовок, а heave - только один раз в свой файл .cpp. Это также означает, что все функции в файле .cpp видят все объявления в заголовке (поэтому порядок больше не имеет значения). Тогда ваш заголовок станет чище, легче для понимания интерфейса класса, в то время как файл .cpp содержит более сложные детали реализации, которые компилируются только один раз (вместо того, чтобы везде включать ваш заголовок.)

0 голосов
/ 28 апреля 2018

Когда вы делаете:

os<<*this;

перегрузка оператора еще не произошла, и, точнее, компилятору не сообщают, что это произойдет позже в этом коде.

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

Тогда компилятор будет знать, что делать, когда встречает эту строку (os<<*this;).

Но он бы не знал, что такое Circle, верно? Таким образом, вам также необходимо переадресовать объявление класса '.


Более того, вы, вероятно, забыли вернуть свой поток при перегрузке оператора.


Собрав все воедино и используя public область для простоты (но, пожалуйста, прочитайте о friend область для перегрузки операторов здесь ), вы получите:

#include <iostream>
using namespace std;

class Circle;
ostream& operator<<(ostream& os, const Circle& rhs_c);

class Circle {
    public:
    void write(ostream& os) const {
        os<<*this;
    }
};

ostream& operator<<(ostream& os, const Circle& rhs_c) {
    // param 'rhs_c' should be used of course
    os<<"circle";
    return os;
}

int main() {
    Circle c1;
    c1.write(cout);
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...