ฟังก์ชันที่มีชื่อเดียวกัน แต่มีลายเซ็นต่างกันในคลาสที่ได้รับ


92

ฉันมีฟังก์ชันที่มีชื่อเดียวกัน แต่มีลายเซ็นต่างกันในคลาสพื้นฐานและคลาสที่ได้รับ เมื่อฉันพยายามใช้ฟังก์ชันของคลาสพื้นฐานในคลาสอื่นที่สืบทอดมาจากที่ได้รับมาฉันได้รับข้อผิดพลาด ดูรหัสต่อไปนี้:

class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};

ฉันได้รับข้อผิดพลาดต่อไปนี้จากคอมไพเลอร์ gcc:

In member function `void C::bar()': no matching function for call to `C::foo(std::string&)' candidates are: int B::foo(int)

ถ้าฉันย้ายออกint foo(int i){};จากชั้นเรียนBหรือเปลี่ยนชื่อจากชั้นเรียนfoo1ทุกอย่างก็ใช้ได้ดี

ปัญหานี้คืออะไร?


1
ในทางเทคนิคแล้วคำถามนี้ซ้ำกันแต่คำถามนี้มีชื่อและคำตอบที่ดีกว่า
Troubadour

คำตอบ:


79

ฟังก์ชันในคลาสที่ได้รับซึ่งไม่ได้แทนที่ฟังก์ชันในคลาสพื้นฐาน แต่มีชื่อเดียวกันจะซ่อนฟังก์ชันอื่น ๆ ที่มีชื่อเดียวกันในคลาสพื้นฐาน

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

A::foo(s)หากคุณจำเป็นต้องเรียกใช้ฟังก์ชันฐานคุณจะต้องขอบเขตการโทรโดยใช้ โปรดทราบว่าการดำเนินการนี้จะปิดใช้งานกลไกฟังก์ชันเสมือนA::foo(string)ในเวลาเดียวกันด้วย


13
ยังอ่านคำตอบของ litdb: คุณสามารถ 'ยกเลิกการซ่อน' ฟังก์ชันพื้นฐานได้โดยใช้ประโยค 'โดยใช้ A :: foo' ใน B.
xtofl

จริงอยู่ฉันแค่มองหาโซลูชันที่สามารถใช้ได้ที่ไซต์การโทรโดยถือว่าลำดับชั้นฐานเป็นแบบคงที่
CB Bailey

2
อะไรคือพื้นฐานของการอ้างสิทธิ์นี้และตามด้วยคำแนะนำ: "โดยทั่วไปถือว่าเป็นการปฏิบัติที่ไม่ดีที่จะมีฟังก์ชันในคลาสที่ได้รับซึ่งมีชื่อเดียวกับฟังก์ชันในคลาสเบสซึ่งไม่ได้มีวัตถุประสงค์เพื่อแทนที่ฟังก์ชันคลาสเบส สิ่งที่คุณเห็นเป็นไม่ได้มักจะมีพฤติกรรมที่พึงประสงค์. มันมักจะดีกว่าที่จะให้ฟังก์ชั่นที่แตกต่างกันชื่อแตกต่างกัน " จะเกิดอะไรขึ้นถ้าพวกเขากำลังทำสิ่งเดียวกันอย่างมีความหมาย? C ++ ให้วิธีแก้ปัญหาที่เกิดจากสิ่งนี้ตามที่อธิบายโดยคำตอบของ Johannes
Nawaz

109

เป็นเพราะการค้นหาชื่อจะหยุดลงหากพบชื่อในฐานใดฐานหนึ่งของคุณ มันจะไม่ดูเกินกว่าในฐานอื่น ๆ ฟังก์ชันใน B ทำเงาฟังก์ชันใน A คุณต้องประกาศฟังก์ชันของ A อีกครั้งในขอบเขตของ B เพื่อให้ทั้งสองฟังก์ชันสามารถมองเห็นได้จากภายใน B และ C:

class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
    using A::foo;
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};

แก้ไข: คำอธิบายที่แท้จริงที่มาตรฐานให้คือ (จาก 10.2 / 2):

ขั้นตอนต่อไปนี้กำหนดผลลัพธ์ของการค้นหาชื่อในขอบเขตคลาส C. ขั้นแรกทุกการประกาศสำหรับชื่อในคลาสและในแต่ละอ็อบเจ็กต์ย่อยของคลาสพื้นฐานจะถูกพิจารณา ชื่อสมาชิก f ในอ็อบเจ็กต์ย่อยหนึ่ง B ซ่อนชื่อสมาชิก f ในอ็อบเจ็กต์ย่อย A ถ้า A เป็นอ็อบเจ็กต์ย่อยคลาสพื้นฐานของ B การประกาศใด ๆ ที่ซ่อนอยู่จะถูกตัดออกจากการพิจารณา การประกาศเหล่านี้แต่ละรายการที่นำมาใช้โดยการประกาศใช้ถือว่ามาจากวัตถุย่อยแต่ละรายการของ C ที่เป็นประเภทที่มีการประกาศที่กำหนดโดยการประกาศใช้ 96) หากชุดการประกาศผลลัพธ์ไม่ใช่ ทั้งหมดมาจากอ็อบเจ็กต์ย่อยประเภทเดียวกันหรือเซ็ตมีสมาชิกที่ไม่หยุดนิ่งและรวมถึงสมาชิกจากอ็อบเจ็กต์ย่อยที่แตกต่างกันมีความคลุมเครือและโปรแกรมมีรูปแบบไม่ถูกต้อง มิฉะนั้นชุดนั้นจะเป็นผลมาจากการค้นหา

มันมีสิ่งต่อไปนี้ที่จะพูดในที่อื่น (ด้านบน)

สำหรับ id-expression [ บางอย่างเช่น "foo" ] การค้นหาชื่อจะเริ่มต้นในขอบเขตคลาสของสิ่งนี้ สำหรับ ID ที่ผ่านการรับรอง [ เช่น "A :: foo", A คือตัวระบุชื่อที่ซ้อนกัน ] การค้นหาชื่อจะเริ่มต้นในขอบเขตของตัวระบุชื่อที่ซ้อนกัน การค้นหาชื่อจะเกิดขึ้นก่อนการควบคุมการเข้าถึง (3.4 ข้อ 11)

([... ] ใส่โดยฉัน). โปรดทราบว่าหมายความว่าแม้ว่า foo ของคุณใน B จะเป็นแบบส่วนตัว แต่ foo ใน A จะยังไม่พบ (เนื่องจากการควบคุมการเข้าถึงจะเกิดขึ้นในภายหลัง)


litb ขอบคุณสำหรับคำตอบของคุณ แต่เมื่อฉันพยายามรวบรวมรหัสของคุณฉันได้รับ: ไม่สามารถปรับการเข้าถึงvoid A::foo(class basic_string<char,char_traits<char>,allocator<char> >)' in คลาส B ได้เนื่องจากวิธีการท้องถิ่น `` int B :: foo (int) 'ที่มีชื่อเดียวกัน อาจเป็นเพราะฉันใช้ gcc รุ่นเก่า
Igor Oks

1
ใช่แน่นอนคอมไพเลอร์บั๊ก คอมไพเลอร์เก่าเคยใช้ "A :: foo;" แทนที่จะ "ใช้ A :: foo;" แต่ก่อนหน้านี้เลิกใช้งานใน C ++
Johannes Schaub - litb
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.