toán tử << phải nhận chính xác một đối số


91

Ah

#include "logic.h"
...

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

logic.cpp

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

Khi tôi biên dịch, nó nói:

std :: ostream & logic :: operator << (std :: ostream &, A &) 'phải nhận chính xác một đối số.

Vấn đề là gì?

Câu trả lời:


126

Vấn đề là bạn xác định nó bên trong lớp,

a) có nghĩa là đối số thứ hai là ẩn ( this) và

b) nó sẽ không làm những gì bạn muốn nó làm, cụ thể là mở rộng std::ostream.

Bạn phải xác định nó là một hàm miễn phí:

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

8
Ngoài ra, anh ấy khai báo nó như một hàm bạn bè và định nghĩa nó như một hàm thành viên.
asaelr

Như đã đề cập tại en.cppreference.com/w/cpp/language/operators , "quá tải của toán tử >> và toán tử << nhận std :: istream & hoặc std :: ostream & là đối số bên trái được gọi là chèn và toán tử trích xuất. Vì chúng lấy kiểu do người dùng xác định làm đối số bên phải (b trong a @ b), chúng phải được triển khai dưới dạng không phải là thành viên ".
Morteza

49

Một chức năng kết bạn không phải là một chức năng thành viên, vì vậy vấn đề là bạn phải khai báo operator<<là bạn của A:

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

sau đó cố gắng xác định nó như một hàm thành viên của lớp logic

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

Bạn có nhầm lẫn về việc liệu logiclà một lớp hay một không gian tên?

Lỗi là do bạn đã cố gắng xác định một thành viên operator<<nhận hai đối số, có nghĩa là nó nhận ba đối số bao gồm cả thistham số ngầm định . Toán tử chỉ có thể nhận hai đối số, do đó khi bạn viết a << bhai đối số là ab.

Bạn muốn định nghĩa ostream& operator<<(ostream&, const A&)là một hàm không phải là thành phần, chắc chắn không phải là một thành viên của logicnó vì nó không liên quan gì đến lớp đó!

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

3

Tôi gặp phải vấn đề này với các lớp mẫu. Đây là một giải pháp chung hơn mà tôi phải sử dụng:

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

Bây giờ: * Hàm toString () của tôi không thể nội tuyến nếu nó sẽ được giấu trong cpp. * Bạn bị mắc kẹt với một số mã trong tiêu đề, tôi không thể gỡ bỏ nó. * Toán tử sẽ gọi phương thức toString (), nó không nội tuyến.

Phần thân của toán tử << có thể được khai báo trong mệnh đề bạn bè hoặc bên ngoài lớp. Cả hai lựa chọn đều xấu. :(

Có thể tôi đang hiểu sai hoặc thiếu điều gì đó, nhưng chỉ cần khai báo chuyển tiếp mẫu toán tử không liên kết trong gcc.

Điều này cũng hoạt động:

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

Tôi nghĩ rằng bạn cũng có thể tránh các vấn đề tạo mẫu buộc khai báo trong tiêu đề, nếu bạn sử dụng một lớp cha không được tạo mẫu để triển khai toán tử << và sử dụng phương thức toString () ảo.


0

Nếu bạn định nghĩa operator<<là một hàm thành viên, nó sẽ có cú pháp phân tách khác với khi bạn sử dụng một hàm không phải thành viên operator<<. Một không phải thành viên operator<<là một toán tử nhị phân, trong đó một thành viên operator<<là một toán tử một ngôi.

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

Vậy .... bạn thực sự gọi họ như thế nào? Các toán tử theo một số cách khác thường, tôi sẽ thách thức bạn viết operator<<(...)cú pháp trong đầu để làm cho mọi thứ có ý nghĩa.

MyObj mo;

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

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

Hoặc bạn có thể cố gắng gọi toán tử nhị phân không phải thành viên:

MyObj mo;

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

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

Bạn không có nghĩa vụ phải làm cho các toán tử này hoạt động một cách trực quan khi bạn biến chúng thành các hàm thành viên, bạn có thể xác định operator<<(int)dịch chuyển sang trái một số biến thành viên nếu bạn muốn, hiểu rằng mọi người có thể hơi mất cảnh giác, bất kể bạn có bao nhiêu nhận xét viết.

Gần như cuối cùng, có thể đôi khi cả hai phân tách cho một cuộc gọi tổng đài đều hợp lệ, bạn có thể gặp rắc rối ở đây và chúng tôi sẽ hoãn cuộc trò chuyện đó.

Cuối cùng, hãy lưu ý rằng nó có thể kỳ lạ như thế nào khi viết một toán tử thành viên một ngôi được cho là trông giống như một toán tử nhị phân (vì bạn có thể làm cho các toán tử thành viên ảo ..... cũng cố gắng không phát triển và chạy theo con đường này .... )

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

    int value = 8;
};

Cú pháp này sẽ gây khó chịu cho nhiều lập trình viên bây giờ ....

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

Lưu ý cách coutđối số thứ hai trong chuỗi ở đây .... kỳ quặc phải không?

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.