2016-11-01 37 views
7

Tôi muốn implemnent một nhà điều hành < < cho streaming lớp học của tôi (nói nó được đặt tên Paragraph). Lớp Paragraph có một số dữ liệu cá nhân, mà lý do tôi muốn (freestanding) điều hành < < chức năng để trở thành một người bạn. Vì vậy, tôi làm như được đề xuất, ví dụ: here on SO. friend tuyên bố, triển khai operator<< và tất cả đều tốt.Tôi có thực sự cần phải uốn cong ngược lại cho một toán tử << cho một lớp trong không gian tên không?

Nhưng bây giờ tôi muốn đặt đoạn bên trong một không gian tên, nói namespace foo. Nó không còn hoạt động nữa! Nếu tôi viết:

namespace foo { 
class Paragraph { 
    public: 
     explicit Paragraph(std::string const& init) :m_para(init) {} 
     std::string const& to_str() const { return m_para; } 
    private: 
     friend std::ostream & operator<<(std::ostream &os, const Paragraph& p); 
     std::string  m_para; 
}; 
} // namespace foo 

Trình biên dịch cho tôi biết bạn đã kết bạn với foo::operator<<, chứ không phải ::operator<<. Được rồi. Vì vậy, tôi thay thế dòng người bạn với:

friend std::ostream & ::operator<<(std::ostream &os, const Paragraph& p); 

nhưng tôi nhận được một lỗi nữa (từ GCC 5.4.0), nói với tôi ::operator<< đã không có được công bố. Ok, hãy khai báo nó. Công việc này có hoạt động không ?:

namespace foo { 
std::ostream & ::operator<<(std::ostream &os, const foo::Paragraph& p); 
class Paragraph { 
    public: 
     explicit Paragraph(std::string const& init) :m_para(init) {} 
     std::string const& to_str() const { return m_para; } 
    private: 
     friend std::ostream & operator<<(std::ostream &os, const Paragraph& p); 
     std::string  m_para; 
}; 
} // namespace foo 

Không, nó không biết về đoạn văn khi đọc tuyên bố ::operator<. Ok, hãy chuyển tiếp tuyên bố:

namespace foo { 
class Paragraph; 
std::ostream & ::operator<<(std::ostream &os, const foo::Paragraph& p); 
class Paragraph { /* actual definition here */ } } 
std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p) { /* impl */ } 

không may mắn. Tôi gặp lỗi lạ:

f.cpp:23:16: note: candidate: std::ostream& operator<<(std::ostream&, const foo::Paragraph&) 
std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p) 
       ^
f.cpp:11:15: note: candidate: std::ostream& operator<<(std::ostream&, const foo::Paragraph&) 
std::ostream& ::operator<<(std::ostream &os, const foo::Paragraph& p); 

... và ở đây tôi đã nghĩ không gian tên chung và không gian tên :: đều giống nhau ... không? ' (thở dài). Dù sao đi nữa, hãy chuyển tờ khai ra khỏi không gian tên:

class foo::Paragraph; 
std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p); 
namespace foo { class Paragraph { /* actual definition here */ } } 
std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p) { /* impl */ } 

Vẫn chưa đủ, bây giờ tôi gặp lỗi "'foo' chưa được khai báo". (Granshes răng) tốt! Theo cách đó!

namespace foo { class Paragraph; } 
std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p); 

namespace foo { class Paragraph { /* actual definition here */ } } 
std::ostream & operator<<(std::ostream &os, const foo::Paragraph& p) { /* impl */ } 

vì vậy điều này, nhưng không kém phần này, hoạt động. Đó là khủng khiếp! Chắc chắn phải có một số cách tiết kiệm hơn để làm điều đó ... đúng không?

Lưu ý: Giả sử operator<< không thể được gạch chân và phải được xác định riêng.

Trả lời

6

Có vẻ như vấn đề của bạn bắt nguồn từ không nhận ra như thế nào ADL có thể tìm thấy ngay operator<< miễn là nó là như nhau không gian tên là Paragraph. Để mở rộng trên ví dụ đầu tiên của bạn

// this is what you have already 
namespace foo { 
class Paragraph { 
public: 
    explicit Paragraph(std::string const& init) :m_para(init) {} 
    std::string const& to_str() const { return m_para; } 
private: 
    friend std::ostream & operator<<(std::ostream &os, const Paragraph& p); 
    std::string  m_para; 
}; 
} // namespace foo 

// Now we can add a definition here, or in a different TU 
namespace foo { 
std::ostream& operator<<(std::ostream& os, const Paragraph& p) { 
    return os << p.m_para; 
} 
} // namespace foo 


int main() { 
    foo::Paragraph p("hello"); 
    // finds your operator<< using adl 
    std::cout << p << '\n'; 
} 
+1

Câu đầu tiên của bạn chạm vào đầu móng. Hoặc có lẽ tôi nên nói đầu của tôi. – einpoklum

+0

@einpoklum Hơn nữa, đây là giải pháp duy nhất đảm bảo tra cứu tên sẽ thành công. ':: operator <<' sẽ không được tìm thấy trong một số ngữ cảnh, vì nó không phải là một phần của bộ tra cứu ADL. –

3

Chỉ cần đặt định nghĩa của toán tử << trong không gian tên cùng với Paragraph. Tra cứu phụ thuộc vào đối tượng sẽ tìm thấy nó khi bạn chèn một đối tượng Paragraph vào một luồng.

// myheader.h: 
namespace ns { 
    struct x { 
     /* ... */ 
     friend std::ostream& operator<<(std::ostream&, const x&); 
    }; 
} 

// myheader.cpp: 
#include "myheader.h" 
namespace ns { 
    std::ostream& operator<<(std::ostream& os, const x& xx) { 
     os << xx.whatever() << '\n'; 
     return os; 
    } 
} 

Hoặc, nếu nó đủ nhỏ để đảm bảo nội tuyến, chỉ cần làm điều đó:

// myheader.h: 
namespace ns { 
    struct x { 
     /* ... */ 
     friend std::ostream& operator<<(std::ostream&, const x&); 
    }; 
    inline std::ostream& operator<<(std::ostream& os, const x& xx) { 
     os << xx.whatever() << '\n'; 
     return os; 
    } 
} 
Các vấn đề liên quan