Tại sao hai mệnh đề sử dụng phân giải thành cùng loại được xem là không rõ ràng trong gcc


32

Tôi có hai lớp cơ sở với việc sử dụng mệnh đề

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


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

Sau đó tôi khai báo một lớp

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

trình biên dịch sau đó gắn cờ một tham chiếu lỗi đến 'NetworkPacket' không rõ ràng 'sendNetworkPacket (NetworkPacket & ...'

Bây giờ cả hai 'sử dụng mệnh đề' đều giải quyết cùng một lớp Mạng bên dưới: NetworkPacket

và trong thực tế nếu tôi thay thế khai báo phương thức bằng:

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

nó biên dịch tốt.

Tại sao trình biên dịch xử lý mỗi mệnh đề sử dụng như một kiểu riêng biệt mặc dù cả hai đều trỏ đến cùng một kiểu bên dưới. Điều này được ủy quyền bởi tiêu chuẩn hay chúng ta có lỗi trình biên dịch?


Có vẻ như trình biên dịch không đủ thông minh
idris

Điểm quan trọng là trình biên dịch tại thời điểm này chỉ biết rằng tồn tại ba NetworkPacket- trong MultiCmdQueueCallback, trong PlcMsgFactoryImplCallback, trong Mạng. Cái nào nên sử dụng nên được chỉ định. Và tôi không nghĩ rằng việc đặt virtualsẽ giúp được gì ở đây.
theWiseBro

@idris: thay vào đó, bạn có nghĩa là tiêu chuẩn không đủ cho phép. trình biên dịch có quyền làm theo tiêu chuẩn.
Jarod42

@ Jarod42 Trong câu trả lời dưới đây 'từ đồng nghĩa cho loại được biểu thị bằng loại id' vì vậy nếu chúng có cùng loại id thì có thể được phép sử dụng cả hai. dù là standart hay trình biên dịch, có vẻ như ai đó thực sự không đủ thông minh.
idris

một trong những vấn đề của đa thừa kế
Eagle275

Câu trả lời:


28

Trước khi xem loại kết quả bí danh, (và khả năng truy cập)

chúng tôi nhìn vào tên

và thực sự,

NetworkPacket có thể là

  • MultiCmdQueueCallback::NetworkPacket
  • hoặc là PlcMsgFactoryImplCallback::NetworkPacket

Thực tế cả hai đều chỉ ra Networking::NetworkPacketlà không liên quan.

Chúng tôi giải quyết tên đầu tiên, dẫn đến sự mơ hồ.


Trên thực tế, điều này chỉ đúng một phần Nếu tôi thêm sử dụng vào PlcNetwork: | sử dụng NetworkPacket = MultiCmdQueueCallback :: NetworkPacket; Tôi nhận được một lỗi biên dịch vì mệnh đề sử dụng trước đó là riêng tư.
Andrew Goedhart

@AndrewGoedhart Không phải là một mâu thuẫn. Tra cứu tên bắt đầu trong lớp học đầu tiên. Khi trình biên dịch tìm thấy một tên duy nhất ở đó, nó được thỏa mãn.
Aconcagua

Vấn đề của tôi ở đây là tại sao tên lại di chuyển từ một mệnh đề đặt tên riêng trong lớp cơ sở. Nếu tôi xóa một trong các khai báo riêng, tức là một trong các lớp cơ sở có mệnh đề sử dụng riêng và không có khai báo nào khác, lỗi sẽ thay đổi thành 'Gói mạng không đặt tên loại'
Andrew Goedhart

1
@AndrewGoedhart Tra cứu tên (rõ ràng) không xem xét khả năng truy cập. Bạn nhận được cùng một lỗi nếu bạn đặt một công khai và một cái khác ở chế độ riêng tư. Đó là lỗi đầu tiên được phát hiện, vì vậy đó là lỗi đầu tiên được in. Nếu bạn xóa một bí danh, thì vấn đề không rõ ràng sẽ không còn nữa, nhưng vấn đề không thể hiểu được vẫn còn, do đó bạn sẽ nhận được lỗi tiếp theo được in. Nhân tiện, không phải là một thông báo lỗi tốt (MSVC một lần nữa?), GCC chính xác hơn về : error: [...] is private within this context.
Aconcagua

1
@AndrewGoedhart Hãy xem xét các vấn đề sau: class A { public: void f(char, int) { } private: void f(int, char) { } }; void demo() { A a; a.f('a', 'd'); }- không giống nhau, nhưng độ phân giải quá tải hoạt động giống nhau: Hãy xem xét tất cả các chức năng có sẵn, chỉ sau khi chọn một chức năng thích hợp, hãy xem xét tính khả thi ... Trong trường hợp cụ thể, bạn cũng nhận được sự mơ hồ; nếu bạn thay đổi hàm private để chấp nhận hai ký tự, nó sẽ được chọn mặc dù là riêng tư - và bạn gặp phải lỗi biên dịch tiếp theo.
Aconcagua

14

Bạn chỉ có thể giải quyết sự mơ hồ bằng cách chọn thủ công cái nào bạn muốn sử dụng.

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

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

}

Trình biên dịch chỉ tìm các định nghĩa trong các lớp cơ sở. Nếu cùng loại và hoặc bí danh có mặt trong cả hai lớp cơ sở, nó chỉ đơn giản phàn nàn rằng nó không biết nên sử dụng cái nào. Nó không quan trọng nếu loại kết quả là giống nhau hay không.

Trình biên dịch chỉ tìm tên trong bước đầu tiên, hoàn toàn độc lập nếu tên này là hàm, kiểu, bí danh, phương thức hoặc bất cứ thứ gì. Nếu tên không rõ ràng, không có hành động nào được thực hiện từ trình biên dịch! Nó chỉ đơn giản là phàn nàn với thông báo lỗi và dừng lại. Vì vậy, chỉ cần giải quyết sự mơ hồ với tuyên bố sử dụng được đưa ra.


Có một số nghi ngờ về từ ngữ. Nếu nó tìm các định nghĩa , thì nó cũng sẽ xem xét loại đó chứ? Nó sẽ không chỉ tìm kiếm tên (và quên đi cách xác định)? Một số tài liệu tham khảo về tiêu chuẩn sẽ rất tuyệt ...
Aconcagua

Nhận xét cuối cùng này là những gì giải thích chính xác tại sao . Thay thế đoạn cuối bằng nhận xét này và tôi sẽ nâng cấp;)
Aconcagua

Tôi không thể chấp nhận - Tôi không phải là tác giả câu hỏi ... Xin lỗi nếu tôi có thể khiến bạn lo lắng. Chỉ cố gắng cải thiện câu trả lời, vì tôi cảm thấy nó không trả lời câu hỏi cốt lõi của QA trước ...
Aconcagua

@Aconcagua: Ubs, lỗi của tôi :-) Cảm ơn bạn đã cải thiện!
Klaus

Trên thực tế, điều này không hoạt động vì cả hai điều khoản đều là riêng tư. Nếu tôi thêm việc sử dụng vào PlcNetwork: | sử dụng NetworkPacket = MultiCmdQueueCallback :: NetworkPacket; Tôi nhận được một lỗi biên dịch vì mệnh đề sử dụng trước đó là riêng tư. Nhân tiện, nếu tôi tạo một lớp cơ sở bằng cách sử dụng mệnh đề công khai và riêng tư khác, tôi vẫn gặp một lỗi mơ hồ. Tôi nhận được lỗi mơ hồ trên các phương thức không được xác định trong lớp cơ sở.
Andrew Goedhart

8

Từ các tài liệu :

Một khai báo bí danh loại giới thiệu một tên có thể được sử dụng làm từ đồng nghĩa cho loại được biểu thị bởi loại id. Nó không giới thiệu một loại mới và nó không thể thay đổi ý nghĩa của một loại tên hiện có.

Mặc dù, hai usingmệnh đề này đại diện cho cùng một loại, trình biên dịch có hai lựa chọn trong tình huống sau:

void sendNetworkPacket(const NetworkPacket &pdu);

Nó có thể chọn giữa:

  • MultiCmdQueueCallback::NetworkPacket
  • PlcMsgFactoryImplCallback::NetworkPacket

bởi vì nó kế thừa từ cả hai MultiCmdQueueCallbackPlcMsgFactoryImplCallbackcác lớp cơ sở. Một kết quả của độ phân giải tên của trình biên dịch là lỗi mơ hồ mà bạn có. Để khắc phục điều này, bạn cần hướng dẫn rõ ràng trình biên dịch sử dụng cái này hoặc cái khác như thế này:

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

hoặc là

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

Thành thật mà nói, tôi không cảm thấy hài lòng ... Cả hai đều là từ đồng nghĩa cho cùng một loại. Tôi có thể dễ dàng có class C { void f(uint32_t); }; void C::f(unsigned int) { }(cung cấp các bí danh phù hợp). Vậy tại sao một sự khác biệt ở đây? Chúng vẫn cùng loại, được xác nhận bởi trích dẫn của bạn (mà tôi không cho là đủ để giải thích) ...
Aconcagua

@Aconcagua: Sử dụng loại cơ sở hoặc bí danh làm cho không bao giờ có sự khác biệt. Một bí danh không bao giờ là một loại mới. Sự quan sát của bạn không liên quan gì đến sự mơ hồ mà bạn tạo ra bằng cách đưa ra bí danh CÙNG trong hai lớp cơ sở.
Klaus

1
@Aconcagua Tôi nghĩ ví dụ bạn đề cập không tương đương với tình huống từ câu hỏi
NutCracker

Chà, hãy thay đổi một chút: Hãy đặt tên cho các lớp A, B và C và typedef D, sau đó bạn thậm chí có thể làm: class C : public A, public B { void f(A::D); }; void C::f(B::D) { }- ít nhất là GCC chấp nhận.
Aconcagua

Tác giả câu hỏi đã hỏi theo nghĩa đen 'Tại sao trình biên dịch coi mỗi mệnh đề sử dụng là một kiểu riêng biệt mặc dù cả hai đều trỏ đến cùng một kiểu cơ bản?' - và tôi không thấy cách trích dẫn sẽ làm rõ lý do tại sao , thay vào đó, nó chỉ xác nhận sự nhầm lẫn của QA ... Đừng muốn nói câu trả lời là sai , nhưng nó không làm rõ đủ trong mắt tôi .. .
Aconcagua

2

Có hai lỗi:

  1. Truy cập bí danh loại riêng
  2. Tham chiếu mơ hồ để gõ bí danh

riêng tư

Tôi không thấy vấn đề mà trình biên dịch phàn nàn về vấn đề thứ hai trước tiên vì thứ tự không thực sự quan trọng - bạn phải sửa cả hai vấn đề để tiếp tục.

công cộng

Nếu bạn thay đổi mức độ hiển thị của cả hai MultiCmdQueueCallback::NetworkPacketPlcMsgFactoryImplCallback::NetworkPacketthành công khai hoặc được bảo vệ, thì vấn đề thứ hai (sự mơ hồ) là rõ ràng - đó là hai bí danh loại khác nhau mặc dù chúng có cùng kiểu dữ liệu cơ bản. Một số người có thể nghĩ rằng trình biên dịch "thông minh" có thể giải quyết điều này (một trường hợp cụ thể) cho bạn, nhưng hãy nhớ rằng trình biên dịch cần phải "nghĩ chung" và đưa ra quyết định dựa trên các quy tắc toàn cầu thay vì đưa ra các trường hợp ngoại lệ cụ thể. Hãy tưởng tượng trường hợp sau:

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


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

Trình biên dịch nên xử lý cả hai NetworkPacketIDnhư nhau? Chắc chắn là không. Bởi vì trên hệ thống size_t32 bit, dài 32 bit trong khi uint64_tluôn là 64 bit. Nhưng nếu chúng ta muốn trình biên dịch kiểm tra các kiểu dữ liệu cơ bản, thì nó không thể phân biệt các kiểu dữ liệu trên hệ thống 64 bit.

công tư

Tôi tin rằng ví dụ này không có ý nghĩa gì trong trường hợp sử dụng của OP, nhưng vì ở đây chúng tôi đang giải quyết các vấn đề nói chung, hãy xem xét rằng:

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

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

Tôi nghĩ rằng trong trường hợp này trình biên dịch nên đối xử PlcNetwork::NetworkPacketnhư PlcMsgFactoryImplCallback::NetworkPacketvì nó không có choises khác. Tại sao nó vẫn từ chối làm như vậy và đổ lỗi cho sự mơ hồ là một điều sai lầm với tôi.


"Tại sao nó vẫn từ chối làm như vậy và đổ lỗi cho sự mơ hồ là một điều sai lầm đối với tôi." Trong C ++, tra cứu tên (khả năng hiển thị) trước kiểm tra truy cập. IIRC, tôi đã đọc ở đâu đó rằng lý do là việc thay đổi tên từ riêng tư thành công khai không nên phá vỡ mã hiện có, nhưng tôi không hoàn toàn chắc chắn.
LF
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.