ตัวดำเนินการ << ต้องใช้อาร์กิวเมนต์เดียว


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) จึงต้องนำไปใช้ในฐานะที่ไม่ใช่สมาชิก "
Morteza

49

ฟังก์ชันเพื่อนไม่ใช่ฟังก์ชันสมาชิกดังนั้นปัญหาคือคุณประกาศoperator<<ว่าเป็นเพื่อนของA:

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

จากนั้นพยายามกำหนดให้เป็นฟังก์ชันสมาชิกของคลาส logic

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

คุณสับสนว่าlogicเป็นคลาสหรือเนมสเปซหรือไม่?

ข้อผิดพลาดเกิดจากคุณได้พยายามกำหนดสมาชิกoperator<<โดยใช้อาร์กิวเมนต์สองตัวซึ่งหมายความว่าต้องใช้อาร์กิวเมนต์สามอาร์กิวเมนต์รวมทั้งthisพารามิเตอร์นัย ผู้ประกอบการสามารถใช้สองอาร์กิวเมนต์เพื่อที่ว่าเมื่อคุณเขียนa << bทั้งสองมีปากเสียงมีและab

คุณต้องการกำหนด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 () แต่จะไม่อินไลน์

เนื้อหาของผู้ดำเนินการ << สามารถประกาศได้ในประโยคเพื่อนหรือนอกชั้นเรียน ตัวเลือกทั้งสองน่าเกลียด :(

บางทีฉันอาจจะเข้าใจผิดหรือพลาดอะไรไป แต่แค่ประกาศเทมเพลตตัวดำเนินการไปข้างหน้าไม่ได้ลิงก์ใน 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;
    }
}

ฉันคิดว่าคุณสามารถหลีกเลี่ยงปัญหาเกี่ยวกับเทมเพลตที่บังคับให้มีการประกาศในส่วนหัวได้หากคุณใช้คลาสพาเรนต์ที่ไม่ได้เทมเพลตเพื่อใช้โอเปอเรเตอร์ << และใช้วิธี virtual 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อาร์กิวเมนต์ที่สองในห่วงโซ่เป็นอย่างไร .... แปลกใช่มั้ย?

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