วิธีการโอเวอร์โหลดโอเปอเรเตอร์ << สำหรับ ostream อย่างถูกต้องหรือไม่


237

ฉันกำลังเขียนไลบรารีเมทริกซ์ขนาดเล็กใน C ++ สำหรับการดำเนินการเมทริกซ์ อย่างไรก็ตามคอมไพเลอร์ของฉันบ่นซึ่งก่อนหน้านั้นไม่ได้ รหัสนี้ถูกวางไว้บนหิ้งเป็นเวลา 6 เดือนและในระหว่างที่ฉันอัพเกรดคอมพิวเตอร์ของฉันจากเดเบียนจำหลักเป็นเลนนี่ (g ++ (เดเบียน 4.3.2-1.1) 4.3.2) แต่ฉันมีปัญหาเดียวกันในระบบ Ubuntu ที่มี g ++ เดียวกัน .

นี่คือส่วนที่เกี่ยวข้องของคลาสเมทริกซ์ของฉัน:

namespace Math
{
    class Matrix
    {
    public:

        [...]

        friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix);
    }
}

และ "การใช้งาน":

using namespace Math;

std::ostream& Matrix::operator <<(std::ostream& stream, const Matrix& matrix) {

    [...]

}

นี่เป็นข้อผิดพลาดที่คอมไพเลอร์กำหนด:

matrix.cpp: 459: ข้อผิดพลาด: 'std :: ostream & Math :: matrix :: โอเปอเรเตอร์ << (std :: ostream &, const Math :: Matrix &) ต้องใช้อาร์กิวเมนต์หนึ่งตัว

ฉันสับสนเล็กน้อยจากข้อผิดพลาดนี้ แต่แล้ว C ++ ของฉันอีกครั้งได้รับสนิมเล็กน้อยหลังจากทำ Java จำนวนมากใน 6 เดือน :-)

คำตอบ:


127

friendคุณได้ประกาศฟังก์ชั่นของคุณเป็น มันไม่ได้เป็นสมาชิกของชั้นเรียน คุณควรลบออกMatrix::จากการใช้งาน friendหมายความว่าฟังก์ชันที่ระบุ (ซึ่งไม่ใช่สมาชิกของคลาส) สามารถเข้าถึงตัวแปรสมาชิกส่วนตัวได้ วิธีที่คุณใช้งานฟังก์ชั่นนั้นเป็นเหมือนวิธีการMatrixเรียนที่ผิด


7
และคุณควรประกาศภายในเนมสเปซคณิตศาสตร์ (ไม่ใช่เฉพาะกับคณิตศาสตร์เนมสเปซที่ใช้)
David Rodríguez - dribeas

1
ทำไมoperator<<ต้องอยู่ใน namespace ของMath? ดูเหมือนว่าควรอยู่ในเนมสเปซส่วนกลาง ฉันยอมรับว่าคอมไพเลอร์ของฉันต้องการให้อยู่ในเนมสเปซของMathแต่ก็ไม่สมเหตุสมผลสำหรับฉัน
Mark Lakata

ขออภัยฉันไม่เห็นว่าทำไมเราจึงใช้คำหลักเป็นเพื่อนที่นี่ เมื่อประกาศการแทนที่โอเปอเรเตอร์เพื่อนในคลาสดูเหมือนว่าเราไม่สามารถนำไปใช้กับเมทริกซ์ :: โอเปอเรเตอร์ << (ostream & os, const Matrix & m) แต่เราต้องการเพียงแค่ใช้โอเปอเรเตอร์การแทนที่โอเปอเรเตอร์ทั่วโลก << ostream & os, const Matrix & m) ดังนั้นทำไมถึงต้องประกาศในคลาสในตอนแรก
Patrick

139

เพียงแค่บอกคุณเกี่ยวกับความเป็นไปได้อื่น: ฉันชอบใช้คำจำกัดความของเพื่อนสำหรับสิ่งนั้น:

namespace Math
{
    class Matrix
    {
    public:

        [...]

        friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix) {
            [...]
        }
    };
}

ฟังก์ชันจะถูกกำหนดเป้าหมายโดยอัตโนมัติในเนมสเปซที่ล้อมรอบMath(แม้ว่าคำจำกัดความจะปรากฏภายในขอบเขตของคลาสนั้น) แต่จะไม่สามารถมองเห็นได้เว้นแต่คุณจะเรียกโอเปอเรเตอร์ << ด้วยวัตถุเมทริกซ์ซึ่งจะทำให้การค้นหา บางครั้งสามารถช่วยในการโทรที่ไม่ชัดเจนเนื่องจากไม่สามารถมองเห็นได้สำหรับประเภทอาร์กิวเมนต์อื่นที่ไม่ใช่เมทริกซ์ Math::Matrix<TypeA, N>เมื่อมีการเขียนคำจำกัดความของคุณยังสามารถดูโดยตรงกับชื่อที่กำหนดไว้ในเมทริกซ์และเมทริกซ์ตัวเองโดยไม่ต้องมีคุณสมบัติตามชื่อที่มีบางส่วนคำนำหน้ายาวอาจจะเป็นและให้พารามิเตอร์แม่แบบเช่น


77

ในการเพิ่มคำตอบ Mehrdad

namespace Math
{
    class Matrix
    {
       public:

       [...]


    }   
    std::ostream& operator<< (std::ostream& stream, const Math::Matrix& matrix);
}

ในการดำเนินงานของคุณ

std::ostream& operator<<(std::ostream& stream, 
                     const Math::Matrix& matrix) {
    matrix.print(stream); //assuming you define print for matrix 
    return stream;
 }

4
ฉันไม่เข้าใจว่าทำไมการลงคะแนนครั้งนี้จึงเป็นการชี้แจงว่าคุณสามารถประกาศโอเปอเรเตอร์ให้อยู่ในเนมสเปซและไม่ใช่แม้แต่ในฐานะเพื่อนและวิธีที่คุณสามารถประกาศโอเปอเรเตอร์
kal

2
คำตอบ Mehrdad ไม่มีตัวอย่างของรหัสใด ๆ ดังนั้นฉันเพิ่งเพิ่มสิ่งที่อาจทำงานได้โดยการย้ายนอกชั้นเรียนในเนมสเปซเอง
kal

ฉันเข้าใจประเด็นของคุณฉันแค่ดูตัวอย่างที่สองของคุณ แต่ตอนนี้ฉันเห็นคุณพาพนักงานออกจากชั้นเรียน ขอบคุณสำหรับคำแนะนำ
Matthias van der Vlies

7
ไม่เพียง แต่จะไม่ได้อยู่ในชั้นเรียน แต่มีการกำหนดไว้อย่างถูกต้องภายในเนมสเปซคณิตศาสตร์ นอกจากนี้ยังมีข้อดีเพิ่มเติม (อาจไม่ใช่เมทริกซ์ แต่มีคลาสอื่น ๆ ) ที่ 'การพิมพ์' อาจเป็นเสมือนจริงดังนั้นการพิมพ์จะเกิดขึ้นในระดับที่สืบทอดมามากที่สุด
David Rodríguez - dribeas

68

สมมติว่าเรากำลังพูดถึงการโอเวอร์โหลดoperator <<สำหรับคลาสทั้งหมดที่ได้รับจากstd::ostreamเพื่อจัดการกับMatrixคลาส (และไม่ให้มากเกินไป<<สำหรับMatrixคลาส) มันเหมาะสมกว่าที่จะประกาศฟังก์ชันโอเวอร์โหลดนอกเนมสเปซคณิตศาสตร์ในส่วนหัว

ใช้ฟังก์ชั่นเพื่อนเฉพาะในกรณีที่ฟังก์ชั่นไม่สามารถทำได้ผ่านอินเทอร์เฟซสาธารณะ

Matrix.h

namespace Math { 
    class Matrix { 
        //...
    };  
}
std::ostream& operator<<(std::ostream&, const Math::Matrix&);

โปรดทราบว่าตัวดำเนินการเกินพิกัดจะประกาศนอกเนมสเปซ

Matrix.cpp

using namespace Math;
using namespace std;

ostream& operator<< (ostream& os, const Matrix& obj) {
    os << obj.getXYZ() << obj.getABC() << '\n';
    return os;
}

ในทางกลับกันหากฟังก์ชั่นโอเวอร์โหลดของคุณไม่จำเป็นต้องมีเพื่อนเช่นต้องการเข้าถึงสมาชิกส่วนตัวและสมาชิกที่ได้รับความคุ้มครอง

math.h

namespace Math {
    class Matrix {
        public:
            friend std::ostream& operator<<(std::ostream&, const Matrix&);
    };
}

คุณจำเป็นต้องแนบคำนิยามฟังก์ชั่นที่มีบล็อก namespace using namespace Math;แทนเพียง

Matrix.cpp

using namespace Math;
using namespace std;

namespace Math {
    ostream& operator<<(ostream& os, const Matrix& obj) {
        os << obj.XYZ << obj.ABC << '\n';
        return os;
    }                 
}

38

ใน C ++ 14 คุณสามารถใช้เทมเพลตต่อไปนี้เพื่อพิมพ์วัตถุใด ๆ ที่มี T :: print (std :: ostream &) const; สมาชิก.

template<class T>
auto operator<<(std::ostream& os, T const & t) -> decltype(t.print(os), os) 
{ 
    t.print(os); 
    return os; 
} 

ใน C ++ 20 Concepts สามารถใช้ได้

template<typename T>
concept Printable = requires(std::ostream& os, T const & t) {
    { t.print(os) };
};

template<Printable T>
std::ostream& operator<<(std::ostream& os, const T& t) { 
    t.print(os); 
    return os; 
} 

ทางออกที่น่าสนใจ! คำถามหนึ่ง - ผู้ประกอบการนี้ควรจะประกาศที่ไหนเช่นในขอบเขตทั่วโลก? ฉันคิดว่ามันควรจะมองเห็นได้ในทุกประเภทที่สามารถใช้ในการทำให้เป็นเทมเพลตได้หรือไม่
บาร์นี

@ barney มันอาจจะอยู่ใน namespace ของคุณเองพร้อมกับชั้นเรียนที่ใช้มัน
QuentinUK

คุณไม่สามารถส่งคืนstd::ostream&ได้เนื่องจากเป็นประเภทส่งคืนหรือไม่
Jean-Michaël Celerier

5
@ Jean-MichaëlCelerierรูปแบบทำให้แน่ใจว่าโอเปอเรเตอร์นี้จะใช้เฉพาะเมื่อ t :: print อยู่ มิฉะนั้นจะพยายามรวบรวมเนื้อหาของฟังก์ชันและให้ข้อผิดพลาดในการรวบรวม
QuentinUK

เพิ่มเวอร์ชันแนวคิดแล้วทดสอบที่นี่godbolt.org/z/u9fGbK
QuentinUK
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.