เหตุใดจึงมีการใช้สองประโยคในการแก้ไขประเภทเดียวกันที่เห็นว่ามีความน่าสงสัยใน gcc


32

ฉันมีคลาสพื้นฐานสองคลาสที่ใช้ส่วนคำสั่ง

 class MultiCmdQueueCallback {
  using NetworkPacket  = Networking::NetworkPacket;
  ....
 }


 class PlcMsgFactoryImplCallback {
   using NetworkPacket = Networking::NetworkPacket;
  ....
 }

ฉันประกาศชั้นเรียน

class PlcNetwork : 
  public RouterCallback, 
  public PlcMsgFactoryImplCallback, 
  public MultiCmdQueueCallback {
  private:
    void sendNetworkPacket(const NetworkPacket &pdu);
}

คอมไพเลอร์ตั้งค่าสถานะการอ้างอิงข้อผิดพลาด 'NetworkPacket' ไม่ชัดเจน 'sendNetworkPacket (NetworkPacket & ... '

ตอนนี้ทั้งสองใช้ 'การใช้คำสั่ง' เพื่อเครือข่ายระดับพื้นฐานเดียวกัน: NetworkPacket

และในความเป็นจริงถ้าฉันแทนที่การประกาศวิธีการด้วย:

 void sendNetworkPacket(const Networking::NetworkPacket &pdu);

มันรวบรวมดี

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


ดูเหมือนว่าคอมไพเลอร์ไม่ฉลาดพอ
idris

จุดที่คอมไพเลอร์ ณ จุดนี้เพิ่งรู้ว่ามีสามNetworkPacket- ใน MultiCmdQueueCallback ใน PlcMsgFactoryImplCallback ในระบบเครือข่าย ควรระบุหนึ่งอันที่จะใช้ และฉันไม่คิดว่าการวางvirtualจะเป็นประโยชน์ใด ๆ ที่นี่
theWiseBro

@idris: แทนคุณหมายถึงมาตรฐานไม่อนุญาตเพียงพอ คอมไพเลอร์มีสิทธิที่จะปฏิบัติตามมาตรฐาน
Jarod42

@ Jarod42 ด้านล่างคำตอบ 'คำพ้องความหมายสำหรับประเภทที่ระบุโดยประเภท -ID' ดังนั้นหากพวกเขามีประเภทเดียวกัน ID ก็สามารถได้รับอนุญาตให้ใช้ทั้ง ไม่ว่าจะเป็นรูปแบบหรือคอมไพเลอร์มันก็ดูเหมือนว่ามีคนไม่ฉลาดพอจริง
idris

หนึ่งในปัญหาของการสืบทอดหลายทาง
eagle275

คำตอบ:


28

ก่อนที่จะดูประเภทผลลัพธ์ของนามแฝง (และการเข้าถึงได้ง่าย)

เราดูที่ชื่อ

และแน่นอน

NetworkPacket อาจจะ

  • MultiCmdQueueCallback::NetworkPacket
  • หรือ PlcMsgFactoryImplCallback::NetworkPacket

ข้อเท็จจริงที่พวกเขาทั้งสองชี้ไปNetworking::NetworkPacketนั้นไม่เกี่ยวข้องกัน

เราทำการแก้ไขชื่อซึ่งส่งผลให้เกิดความกำกวม


จริง ๆ แล้วนี่เป็นเพียงบางส่วนเท่านั้นหากฉันเพิ่มการใช้งานใน PlcNetwork: | using NetworkPacket = MultiCmdQueueCallback :: NetworkPacket; ฉันได้รับข้อผิดพลาดของคอมไพเลอร์เพราะข้อก่อนหน้านี้ใช้เป็นส่วนตัว
Andrew Goedhart

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

ปัญหาของฉันที่นี่คือเหตุผลว่าทำไมชื่อที่นำเสนอจากส่วนคำสั่งการตั้งชื่อส่วนตัวในชั้นฐาน ถ้าฉันลบการประกาศส่วนตัวหนึ่งรายการดังนั้นหนึ่งในคลาสพื้นฐานที่มีการใช้ประโยคส่วนตัวและไม่มีการเปลี่ยนแปลงข้อผิดพลาด 'แพ็คเก็ตเครือข่ายไม่ได้ตั้งชื่อประเภท'
Andrew Goedhart

1
@AndrewGoedhart ชื่อการค้นหา (ชัด) ไม่พิจารณาการเข้าถึง คุณได้รับข้อผิดพลาดเดียวกันหากคุณทำให้เป็นสาธารณะและอีกอันหนึ่งเป็นส่วนตัว นั่นเป็นข้อผิดพลาดแรกที่จะค้นพบดังนั้นนั่นเป็นข้อผิดพลาดแรกที่จะพิมพ์ หากคุณลบนามแฝงหนึ่งรายการปัญหาของความกำกวมนั้นจะหายไป แต่ไม่มีการทดแทนอย่างใดอย่างหนึ่งยังคงอยู่ดังนั้นคุณจะได้รับข้อผิดพลาดถัดไปที่พิมพ์ออกมา โดยวิธีการที่ไม่ได้เป็นข้อผิดพลาดที่ดี (? MSVC อีกครั้ง) GCC error: [...] is private within this contextมีมากขึ้นเป็นที่แม่นยำมากขึ้นเกี่ยวกับ:
Aconcagua

1
@AndrewGoedhart พิจารณาต่อไปนี้: class A { public: void f(char, int) { } private: void f(int, char) { } }; void demo() { A a; a.f('a', 'd'); }- ไม่เหมือนกัน แต่การแก้ปัญหาการทำงานเกินพิกัดเหมือนกัน: พิจารณาฟังก์ชั่นที่มีอยู่ทั้งหมดหลังจากเลือกหนึ่งที่เหมาะสมพิจารณาความเหมาะสม ... ในกรณีที่คุณได้รับความกำกวมเช่นกัน; หากคุณเปลี่ยนฟังก์ชั่น privat เพื่อยอมรับสองตัวอักษรมันจะถูกเลือกแม้ว่าจะเป็นแบบส่วนตัว - และคุณพบข้อผิดพลาดในการรวบรวมครั้งต่อไป
Aconcagua

14

คุณสามารถแก้ไขความคลุมเครือด้วยตนเองโดยเลือกสิ่งที่คุณต้องการใช้ด้วยตนเอง

class PlcNetwork : 
  public RouterCallback, 
  public PlcMsgFactoryImplCallback, 
  public MultiCmdQueueCallback {

using NetworkPacket= PlcMsgFactoryImplCallback::NetworkPacket; // <<< add this line
private:
    void sendNetworkPacket(const NetworkPacket &pdu);

}

คอมไพเลอร์มองหาคำจำกัดความในคลาสพื้นฐานเท่านั้น หากประเภทและนามแฝงเดียวกันมีอยู่ในคลาสฐานทั้งสองเพียงแค่บ่นว่าไม่ทราบว่าจะใช้ประเภทใด ไม่สำคัญว่าผลลัพธ์ที่ได้จะเหมือนกันหรือไม่

คอมไพเลอร์จะค้นหาชื่อในขั้นตอนแรกเท่านั้นโดยไม่ขึ้นอยู่กับว่าชื่อนี้เป็นฟังก์ชันประเภทนามแฝงวิธีการหรืออะไรก็ตาม หากชื่อไม่ชัดเจนจะไม่มีการดำเนินการใด ๆ เพิ่มเติมจากคอมไพเลอร์! มันก็บ่นกับข้อผิดพลาดและหยุด ดังนั้นเพียงแก้ไขความคลุมเครือด้วยการใช้ข้อความสั่ง


มีข้อสงสัยเกี่ยวกับถ้อยคำ ถ้ามันค้นหาคำจำกัดความมันจะไม่พิจารณาประเภทด้วยหรือไม่? มันจะไม่ค้นหาชื่อเท่านั้น (และลืมเกี่ยวกับวิธีการกำหนด) การอ้างอิงถึงมาตรฐานจะดีมาก ...
Aconcagua

ความคิดเห็นสุดท้ายนี้เป็นสิ่งที่อธิบายว่าทำไมอย่างถูกต้อง แทนที่ย่อหน้าสุดท้ายด้วยความคิดเห็นนี้และฉันจะอัปโหลด;)
Aconcagua

ฉันไม่สามารถยอมรับได้ - ฉันไม่ใช่ผู้เขียนคำถาม ... ขออภัยถ้าฉันรู้สึกเครียด เพียงพยายามปรับปรุงคำตอบเพราะฉันรู้สึกว่ามันไม่ได้ตอบคำถามหลักของ QA มาก่อน ...
Aconcagua

@Aconcagua: Ubs ความผิดของฉัน :-) ขอบคุณสำหรับการปรับปรุง!
Klaus

ที่จริงแล้วสิ่งนี้ไม่ได้ผลเพราะทั้งคู่ใช้คำสั่งเป็นส่วนตัว ถ้าฉันเพิ่มการใช้กับ PlcNetwork: | using NetworkPacket = MultiCmdQueueCallback :: NetworkPacket; ฉันได้รับข้อผิดพลาดของคอมไพเลอร์เพราะข้อก่อนหน้านี้ใช้เป็นส่วนตัว โดยวิธีถ้าฉันทำให้ชั้นฐานหนึ่งโดยใช้ประโยคสาธารณะและส่วนตัวอื่น ๆ ฉันยังคงได้รับข้อผิดพลาดความกำกวม ฉันได้รับข้อผิดพลาดความกำกวมเกี่ยวกับวิธีการที่ไม่ได้กำหนดไว้ในคลาสฐาน
Andrew Goedhart

8

จากเอกสาร :

การประกาศชื่อแทนประเภทแนะนำชื่อซึ่งสามารถใช้เป็นคำพ้องความหมายสำหรับประเภทที่แสดงโดย type-id ไม่แนะนำชนิดใหม่และไม่สามารถเปลี่ยนความหมายของชื่อประเภทที่มีอยู่

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

void sendNetworkPacket(const NetworkPacket &pdu);

สามารถเลือกระหว่าง:

  • MultiCmdQueueCallback::NetworkPacket และ
  • PlcMsgFactoryImplCallback::NetworkPacket

เพราะมันสืบทอดมาจากคลาสทั้งสองMultiCmdQueueCallbackและPlcMsgFactoryImplCallbackฐาน ผลลัพธ์ของการแก้ไขชื่อคอมไพเลอร์คือข้อผิดพลาดที่คลุมเครือที่คุณมี ในการแก้ไขปัญหานี้คุณต้องสั่งให้คอมไพเลอร์ใช้อย่างใดอย่างหนึ่งดังนี้:

void sendNetworkPacket(const MultiCmdQueueCallback::NetworkPacket &pdu);

หรือ

void sendNetworkPacket(const PlcMsgFactoryImplCallback::NetworkPacket &pdu);

พูดตามตรงฉันไม่รู้สึกพอใจ ... พวกเขาทั้งคู่ต่างมีความหมายเหมือนกัน ฉันสามารถมีได้อย่างง่ายดายclass C { void f(uint32_t); }; void C::f(unsigned int) { }(ให้ตรงกับนามแฝง) เหตุใดจึงมีความแตกต่างที่นี่ พวกเขายังคงเป็นประเภทเดียวกันยืนยันโดยการอ้างอิงของคุณ (ซึ่งฉันไม่พิจารณาเพียงพอที่จะอธิบาย) ...
Aconcagua

@Aconcagua: การใช้ประเภทฐานหรือชื่อแทนทำให้ไม่มีความแตกต่าง นามแฝงไม่เคยเป็นประเภทใหม่ การสังเกตของคุณไม่เกี่ยวข้องกับความคลุมเครือที่คุณสร้างโดยให้นามแฝงเดียวกันในสองคลาสพื้นฐาน
Klaus

1
@Aconcagua ฉันคิดว่าตัวอย่างที่คุณกล่าวถึงไม่ถูกต้องเทียบเท่ากับสถานการณ์จากคำถาม
NutCracker

ทีนี้ลองเปลี่ยนนิดหน่อย: ลองตั้งชื่อคลาส A, B และ C และ typedef D จากนั้นคุณก็ทำได้: class C : public A, public B { void f(A::D); }; void C::f(B::D) { }- อย่างน้อย GCC ก็ยอมรับ
Aconcagua

ผู้เขียนคำถามอย่างแท้จริงถามว่า'ทำไมคอมไพเลอร์ปฏิบัติต่อแต่ละประโยคโดยใช้ประโยคเป็นประเภทที่แตกต่างกันถึงแม้ว่าพวกเขาทั้งสองชี้ไปที่ประเภทพื้นฐานเดียวกัน' - และฉันไม่เห็นว่าการอ้างอิงจะอธิบายถึงสาเหตุได้อย่างไรเพียง แต่เป็นการยืนยันความสับสนของ QA ... ไม่ต้องการที่จะพูดคำตอบนั้นผิดแต่มันไม่ชัดเจนพอในสายตาของฉัน .. .
Aconcagua

2

มีข้อผิดพลาดสองประการ:

  1. การเข้าถึงนามแฝงประเภทส่วนตัว
  2. การอ้างอิงที่ไม่ชัดเจนในการพิมพ์นามแฝง

เอกชนและเอกชน

ฉันไม่เห็นปัญหาที่คอมไพเลอร์บ่นเกี่ยวกับปัญหาที่สองก่อนเพราะลำดับไม่สำคัญ - คุณต้องแก้ไขปัญหาทั้งสองเพื่อดำเนินการต่อ

ภาครัฐและประชาชน

หากคุณเปลี่ยนการเปิดเผยของทั้งสองMultiCmdQueueCallback::NetworkPacketและ PlcMsgFactoryImplCallback::NetworkPacketเป็นสาธารณะหรือได้รับการป้องกันปัญหาที่สอง (ความคลุมเครือ) จะชัดเจน - เป็นนามแฝงประเภทที่แตกต่างกันสองประเภทแม้ว่าพวกเขาจะมีชนิดข้อมูลพื้นฐานเดียวกัน บางคนอาจคิดว่าคอมไพเลอร์ "ฉลาด" สามารถแก้ปัญหานี้ (กรณีเฉพาะ) สำหรับคุณ แต่โปรดทราบว่าคอมไพเลอร์ต้อง "คิดโดยทั่วไป" และตัดสินใจตามกฎสากลแทนที่จะทำการยกเว้นเฉพาะกรณี ลองนึกภาพกรณีต่อไปนี้:

class MultiCmdQueueCallback {
    using NetworkPacketID  = size_t;
    // ...
};


class PlcMsgFactoryImplCallback {
    using NetworkPacketID = uint64_t;
    // ...
};

ผู้แปลควรปฏิบัติต่อทั้งคู่NetworkPacketIDเหมือนกันหรือไม่? ไม่แน่ใจ เนื่องจากในระบบsize_t32 บิตยาว 32 บิตในขณะที่uint64_tเป็น 64- บิตเสมอ แต่ถ้าเราต้องการให้คอมไพเลอร์ตรวจสอบชนิดข้อมูลพื้นฐานมันก็ไม่สามารถแยกความแตกต่างเหล่านั้นบนระบบ 64 บิต

ภาครัฐและเอกชน

ฉันเชื่อว่าตัวอย่างนี้ไม่สมเหตุสมผลในกรณีการใช้ของ OP แต่เนื่องจากที่นี่เรากำลังแก้ไขปัญหาโดยทั่วไปให้พิจารณาดังนี้

class MultiCmdQueueCallback {
private:
    using NetworkPacket  = Networking::NetworkPacket;
    // ...
};

class PlcMsgFactoryImplCallback {
public:
    using NetworkPacket  = Networking::NetworkPacket;
    // ...
};

ผมคิดว่าในกรณีนี้คอมไพเลอร์ควรปฏิบัติPlcNetwork::NetworkPacketเป็นPlcMsgFactoryImplCallback::NetworkPacketเพราะมันไม่มีโทนอื่น ๆ ทำไมมันยังคงปฏิเสธที่จะทำเช่นนั้นและโทษความคลุมเครือเป็นเรื่องที่ไม่ดีสำหรับฉัน


"ทำไมมันยังคงปฏิเสธที่จะทำเช่นนั้นและโทษว่าด้วยความกำกวมนั้นเป็นสิ่งที่ไม่ดีสำหรับฉัน" ใน C ++ การค้นหาชื่อ (การมองเห็น) นำหน้าการตรวจสอบการเข้าถึง IIRC ฉันอ่านที่ไหนสักแห่งว่าเหตุผลคือการเปลี่ยนชื่อจากส่วนตัวเป็นสาธารณะไม่ควรทำลายรหัสที่มีอยู่ แต่ฉันไม่แน่ใจทั้งหมด
LF
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.