Nhiều kế thừa dẫn đến quá tải chức năng ảo mơ hồ giả


8

Trong ví dụ này, các lớp FooBarđược cung cấp từ một thư viện. Lớp tôi Bazkế thừa từ cả hai.

struct Foo
{
    void do_stuff (int, int);
};

struct Bar
{
    virtual void do_stuff (float) = 0;
};

struct Baz : public Foo, public Bar
{
    void func ()
    {
        do_stuff (1.1f); // ERROR HERE
    }
};

struct BazImpl : public Baz
{
    void do_stuff (float) override {};
};

int main ()
{
    BazImpl () .func ();
}

Tôi nhận được lỗi biên dịch reference to ‘do_stuff’ is ambiguouscó vẻ giả đối với tôi vì hai chữ ký hàm hoàn toàn khác nhau. Nếu do_stuffkhông phải là ảo tôi có thể gọi Bar::do_stuffđể định hướng nó, nhưng để làm như vậy sẽ phá vỡ tính đa hình và gây ra lỗi liên kết.

Tôi có thể thực hiện funccuộc gọi ảo do_stuffmà không cần đổi tên không?


1
Bạn cần định hướng chức năng lớp cơ sở nào sẽ gọi. Một cái gì đó như thế này
AndyG

Câu trả lời:


10

Bạn có thể làm được việc này:

struct Baz : public Foo, public Bar
{
    using Bar::do_stuff;
    using Foo::do_stuff;
    //...
}

Đã thử nghiệm với Wandbox gcc mới nhất và nó biên dịch tốt. Tôi nghĩ đó là trường hợp tương tự với tình trạng quá tải chức năng, một khi bạn quá tải thì bạn không thể sử dụng các triển khai lớp cơ sở mà không có using.

Trong thực tế điều này không có gì để làm với các chức năng ảo. Ví dụ sau có cùng lỗi GCC 9.2.0 error: reference to 'do_stuff' is ambiguous:

struct Foo
{
    void do_stuff (int, int){}
};

struct Bar
{
    void do_stuff (float) {}
};

struct Baz : public Foo, public Bar
{
    void func ()
    {
        do_stuff (1.1f); // ERROR HERE
    }
};

Câu hỏi liên quan có thể


2

Tra cứu tên và giải quyết quá tải là khác nhau. Tên phải được tìm thấy trong một phạm vi trước tiên, tức là chúng ta phải tìm một Xtên sao cho tên do_stuffđược phân giải thành X::do_stuff- độc lập với việc sử dụng tên - và sau đó giải quyết quá tải chọn giữa các khai báo khác nhau X::do_stuff.

Quá trình KHÔNG xác định tất cả các trường hợp như vậy A::do_stuff , B::do_stuffvv có thể nhìn thấy, và sau đó thực hiện giải quyết tình trạng quá tải giữa các công đoàn về điều đó. Thay vào đó, một phạm vi duy nhất phải được xác định cho tên.

Trong mã này:

struct Baz : public Foo, public Bar
{
    void func ()
    {
        do_stuff (1.1f); // ERROR HERE
    }
};

Baz không chứa tên do_stuff , vì vậy các lớp cơ sở có thể được tra cứu. Nhưng tên xảy ra ở hai cơ sở khác nhau, vì vậy việc tra cứu tên không xác định được phạm vi. Chúng tôi không bao giờ nhận được cho đến khi giải quyết quá tải.

Các sửa chữa được đề xuất trong câu trả lời khác hoạt động vì nó giới thiệu tên do_stuffcho phạm vi Bazvà cũng giới thiệu 2 quá tải cho tên. Vì vậy, tra cứu tên xác định điều đó do_stuffcó nghĩa là Baz::do_stuffvà sau đó giải quyết quá tải chọn từ hai chức năng được gọi là Baz::do_stuff.


Như một bên, bóng là một hậu quả khác của tra cứu tên (bản thân nó không phải là một quy tắc). Tra cứu tên chọn phạm vi bên trong và vì vậy mọi thứ trong phạm vi bên ngoài không khớp.

Một yếu tố phức tạp hơn nữa xảy ra khi tra cứu phụ thuộc vào đối số đang diễn ra. Để tóm tắt rất ngắn gọn, việc tra cứu tên được thực hiện nhiều lần cho một lệnh gọi hàm với các đối số của loại lớp - phiên bản cơ bản như được mô tả trong câu trả lời của tôi, và sau đó một lần nữa cho từng loại đối số. Sau đó, sự kết hợp của các phạm vi được tìm thấy đi vào tập hợp quá tải. Nhưng điều đó không áp dụng cho ví dụ của bạn vì hàm của bạn chỉ có các tham số của kiểu dựng sẵn.

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.