Не удалось понять (и исправить), почему существует это предупреждение «вызов перегрузки xxx неоднозначен» - PullRequest
0 голосов
/ 07 апреля 2019

Я застрял в исправлении этого предупреждения gcc: я получил древовидную версию метода "registerCalBack", каждый из которых принимает различный "вызываемый", введенный через std :: function. В зависимости от различных вызываемых типов, которые я объявляю, я могу скомпилировать или нет, gcc выдает предупреждение «вызов перегруженного registerCallBackxxxxx неоднозначен».

Я знаю, что перегрузка может быть решена компилятором с учетом аргументов, а не типа возвращаемого значения, но в этом случае я не смог понять, почему gcc видит неоднозначность: для меня каждый TCallBack ..., который я определил, различен в своих аргументах, и когда я изменяю тип возвращаемого значения третьего, он компилируется ... это действительно смущает меня. Я предполагаю, что отчасти проблема заключается в том, что некоторые параметры на самом деле имеют неполный тип, но именно так они доступны из заголовков SDL, поэтому я воспроизвел это на примере, который я привожу в этой теме.

В комментарии в коде вы получили примеры определений, которые были скомпилированы, а другие нет.

Надеюсь, некоторые из вас поймут лучше меня, сейчас я не знаю, где искать. Большое спасибо заранее.

здесь командная строка gcc для компиляции:

-pedantic -W -Wall -Wextra -std=c++2a -Weffc++ -Wfatal-errors -Winit-self -Wnon-virtual-dtor -Winline -Wmissing-declarations -Wunreachable-code -Wshadow -Wswitch-enum -fstack-protector -Wstack-protector -O0

P.

#include <iostream>
#include <functional>

//This is how SDL_Renderer and SDL_Texture are declared in SDL.h, as incomplete type declaration, to make it opaque
//I reproduce it here with other name, to avoid the need to install SDL if you want to test
struct RENDERER;
typedef struct RENDERER RENDERER;

struct TEXTURE;
typedef struct TEXTURE TEXTURE;

//this is stupid, just to make test
struct dumb;
typedef struct dumb dumb;

class ClassUsingCallBacks // an instance of this class will use callbacks
{
public:

    typedef std::function < RENDERER* (void) > TCallBack_GetRenderer;
    typedef std::function < TEXTURE* (const std::string&) > TCallBack_GetTexture;

    //this works: 
    // typedef std::function < dumb* (void) >
    // typedef std::function < dumb* (TEXTURE*) >   
   // typedef std::function < int (TEXTURE*) >  
    // typedef std::function < TEXTURE* (TEXTURE*) > 

    // BUT THIS FAILED TO COMPILE : 
    // typdef std::function < void (TEXTURE*) >
    // typdef std::function < void* (TEXTURE*) >
    // typedef std::function < void (const std::string&, int, int, int) 

    typedef std::function < void (TEXTURE*) > TCallBack_removeTexture;

    virtual ~ClassUsingCallBacks() {};

    void registerCallBack(TCallBack_GetRenderer cb) {
        std::cout << "Register a TCallBack_GetRenderer" << std::endl;
        getRenderer = cb;
    }

    void registerCallBack(TCallBack_GetTexture cb) {
        std::cout << "Register a TCallBack_GetTexture" << std::endl;
        getTexture = cb;
    }

    void registerCallBack(TCallBack_removeTexture cb) {
        std::cout << "Register a TCallBack_removeTexture" << std::endl;
        removeTexture = cb;
    }

    //to test registered callbacks
    void makeCalls(void) {
        if (getRenderer) getRenderer();
        if (getTexture)  getTexture("a name");
        //not this one since it's the one we failed to implement :/
        // if (removeTexture) removeTexture();
    }

protected:

    TCallBack_GetRenderer   getRenderer {};
    TCallBack_GetTexture    getTexture  {};
    TCallBack_removeTexture removeTexture {};
};

class ClassWithCallBacks
{
public:

    virtual ~ClassWithCallBacks() {};

    RENDERER* getRenderer(void) {
        std::cout << "Inside getRenderer" << std::endl;
        return nullptr;
    }

    TEXTURE* getTexture(const std::string& s) {
        (void)s;
        std::cout << "Inside getTexture" << std::endl;
        return nullptr;
    }

    void removeTexture(TEXTURE* t) {
        (void)t;
        std::cout << "Inside removeTexture" << std::endl;
    }
};

int main(int argc, char **argv)
{
    (void)argc;
    (void)argv;

    std::cout << "entering main" << std::endl;

   ClassWithCallBacks   calledObject; 
    ClassUsingCallBacks user;

    auto cb_1 = std::bind(&ClassWithCallBacks::getRenderer, calledObject);
    user.registerCallBack(cb_1);    

    auto cb_2 = std::bind(&ClassWithCallBacks::getTexture, calledObject, std::placeholders::_1);
    user.registerCallBack(cb_2);    

    user.makeCalls();

    std::cout << "Leaving main" << std::endl;

    return 0;
}

1 Ответ

0 голосов
/ 08 апреля 2019

std::bind создает вызываемый объект, который принимает произвольное количество аргументов, и просто отбрасывает те, которые не нужно переадресовывать в связанный вызываемый объект.

std::function<void(Something)> принимает вызываемый объект, который возвращает результат, затем просто отбрасывает этот результат.

Следовательно, cb_1 может быть принято как std::function<RENDERER* (void)>, так и std::function<void (TEXTURE*)>.Он может принимать (и игнорировать) параметр TEXTURE*, а функция может игнорировать его возвращаемое значение.


По сути, вы в значительной степени полагаетесь на удаление типов, но затем надеетесь, что типы, которые вы стираете, будуттем не менее, помогите справиться с разрешением перегрузки.Лично я бы назвал три функции registerXXX разными именами, отражая тип обратного вызова, который они регистрируют.Без перегрузки, без проблем.

...