Why overloaded function cannot be assigned to function type object



  • int add(int i, int j) { return i + j; }
    std::string add(const std::string & h1, const std::string & h2) { return std::string(); }
    ......
    std::function<int(int, int)> f = add;
    

    The last line reports an error "cannot convert from 'overloaded-function' to 'std::function<int (int,int)>". The template argument of function clearly specified the call signature as int(int,int), so I think it should select the add function in line 1. I don't understand why the compiler gets confused which overloaded function is the best match.


  • Mod

    Try

    	std::function<int(int, int)> f = static_cast<int(*)(int, int)> (add);
    

    Your version does not work, because the constructor of std::function is a template itself, accepting practically anything. Thus, the compiler cannot figure out from the template parameters to std::function, which way you want to call the constructor, because the constructor argument is unrelated to the class template parameters.

    The above answer is a summary of the answers to a Stackoverflow question that sounds suspiscously like the question you asked, down to almost literally the same exact code example. Your next post better show you are a human and not some bot trained on stackoverlow questions.



  • @SeppJ Based on your answer, if I comment out line 1, the code should work because 1) it forcibly cast call signature (your answer) and 2) there is onlyy one add function left (no overload any more). But the compiler complains with a similar error. How to explain that?

    I have no idea what was happening in Stackoverflow and I am a human with straight gender. Copying answer from other place without really understanding what is asked looks more like a bot.


  • Mod

    @zzzhhh sagte in Why overloaded function cannot be assigned to function type object:

    @SeppJ Based on your answer, if I comment out line 1, the code should work because 1) it forcibly cast call signature (your answer) and 2) there is onlyy one add function left (no overload any more). But the compiler complains with a similar error. How to explain that?

    The first line is the one you want, is it not? It is the second function that is interferring. If you remove the second line, the code will work without the cast.

    Removing the first line will obviously not work, because the remaining string(const string&, const string&) is not compatible to int(int, int).



  • @SeppJ I was not asking how to fix the code so that there is no error; the textbook I read gives me two methods of fixing it. What I am asking is I don't understand why the second add function can confuse the compiler even if tthis overloaded version is incompatible with the needed call signature. Since it is incompatible, why didn't the compiler just skips it and go ahead to the best match (line 1) but utters an error?


  • Mod

    As I said: It is the std::function constructor. Look at the reference:
    https://en.cppreference.com/w/cpp/utility/functional/function/function

    The class std::function<R(Args...)> has (among many others) the constructor template< class F > function( F&& f ). That template parameter F is completely unrelated to the template parameters of the function. It could be anything. Therefore, the compiler has no hint as to which add you could mean, because both fit a generic F&&. Hence, just writing add is ambiguous in this context, you must specify which one you mean.

    That ambiguity must be resolved before the compiler can instantiate the constructor template, after all, it must know what F&& is. If you resolve the ambiguity by removing the first line, the constructor gets instantiated with F as string(string const&, string const&) and then the compiler can see inside that constructor that the combination of int(int, int) and string(string const&, string const&) does not work and raises a different (though similar sounding) error.

    The rules of the language say, that ambiguity is an error. The compiler should not try all possible interpretations of your code and decide that one of them leads to fewer errors down the line, and is therefore correct. Well, it kind of should do this when SFINAE is involved, but there it is a one step process, where the incompatible choices would lead to immediate errors. With the std::function constructor, though, the actual error would happen at some later point. That is something the compiler cannot know while deciding which add is meant, because it is not allowed to look into the future.


  • Mod

    Consider this code:

    int add(int i, int j) { return i + j; }
    std::string add(const std::string & h1, const std::string & h2) { return std::string(); }
    
    template <typename T>
    class MyFunction
    {
    	T &t;
    public:
    	MyFunction(T && t): t(t) {};
    };
    
    int main() {
    	MyFunction<int(int, int)> mf = add;
    }
    

    This works perfectly fine. The T constructor parameter is the same as the class template parameter. Hence the compiler knows in line 12 that something compatible to int(int, int) is required. That leaves only one possible choice of add and the code works fine.

    You might ask "Why is std::function not implemented that way? Even SeppJ could figure out something better!". The answer is, that I did not figure out something better. MyFunction is a pale comparison to std::function. My example is only a function pointer with extra steps, whereas std::function has a much wider range of options as to what exactly its callable is. The code for that has to happen somewhere, and unfortunately it cannot be done before the constructor call is even resolved.



  • Dieser Beitrag wurde gelöscht!


  • OK, I think I figure out the key. When matching overloaded functions, we have a single concrete argument and find the best match according to the type of the given argument, in this orderr. But I confused by reversing this order. The situation in my question is that I have two arguments of different types with the same name, but only one constructor function. So function matching (overload resolution) does not apply at all. The compiler did not know that thus got confused.

    In short, roughly speaking, function matching is 1 argument -> 2 functions. But my question is 2 arguments -> 1 function. Not applicable.


  • Mod

    I am not sure you mean the right thing. But it is correct, that overload resolution does not apply here, and that is indeed why the compiler cannot know what exactly you mean when you refer to add. There is just nothing to go on here to give it a hint.

    I do not know what you mean with your numbers. It's just plain old ambiguity. It does not matter if there are 2, 3, or 2000 candidates.


  • Gesperrt

    @zzzhhh

    Guten Morgen,

    das Problem entsteht durch die Überladung der Funktion add.

    Zusammenfassung des Problems

    Du hast zwei überladene Funktionen:

    int add(int i, int j) {
        return i + j;
    }
    
    std::string add(const std::string& h1, const std::string& h2) {
        return std::string();
    }
    

    Und Du versuchst, eine dieser Funktionen einer std::function zuzuweisen:

    std::function<int(int, int)> f = add;
    

    Hier entsteht der Fehler: "cannot convert from 'overloaded-function' to 'std::function<int (int,int)>".

    Erklärung des Fehlers

    Der Compiler kann nicht bestimmen, welche der überladenen Funktionen add verwendet werden soll, weil std::function ein Template-Konstruktor ist, der praktisch alles akzeptiert. Ohne weitere Hinweise vom Benutzer kann der Compiler nicht entscheiden, welche add-Funktion zuzuweisen ist, weil beide überladenen Varianten zum generischen Template F&& passen.

    Lösung

    Um dies zu lösen, musst du den Funktionszeiger explizit in den gewünschten Typ konvertieren:

    std::function<int(int, int)> f = static_cast<int(*)(int, int)>(add);
    

    Durch diesen Cast wird die richtige Überladung ausgewählt.


Anmelden zum Antworten