Search code examples
c++c++11visual-studio-2013std-functioncompiler-bug

Ambiguity in constructor overload because of std::function


I want to make two overloads of the constructor of some class as follow:

foo(int, std::function<int(Tpoint, Tpoint)>);
foo(int, std::function<int(Tpoint, Tpoint, std::vector<Tpoint>)>);

When calling it, I got ambiguity problem. Why?

foo<cv::Point> bar(2,[](cv::Point const& l, cv::Point const& r){
    return 5;
});

Is not clear that I need the first constructor here since It has only 2 arguments for std::function.

EDIT:

As Lol4t0 comment, it works on another compiler. It seems VS.NET problem. Example 1 , Example 2

I am using Microsoft Visual Studio 2013. Can anyone reproduce?


Solution

  • MSVC 2013 std::function is only a C++11 std::function without an additional feature from (if I remember rightly) a defect report.

    This additional feature is that the template<class F> function(F&&) constructor be SFINAE-like restricted to only work on types F such that the function can actually be constructed from that type. (the standard doesn't mandate a SFINAE based implementation)

    In effect, std::function has a "try constructing from anything" constructor. And when it fails, it fails too late for overload resolution. At least in MSVC2013. So both of your std::functions look like valid overloads.

    I believe the latest version of 2015 has a std::function with limited ability to do the overload you requested.

    In 2013, you can do a workaround that might work, but it is a pain.

    First, learn how to do overload sets of function objects. Then, create an overload set of a function object F and another function object that takes Tpoint, Tpoint and returns some_special_tag_type. Now, pass Tpoint, Tpoint to that overload set, and track if the return type is some_special_tag_type. Tag dispatch on this decision from a foo(int, F&&) overload into a foo_impl(tag_1, int, std::function<blah_1>) and a foo_impl(tag_2, int, std::function<blah_2>).

    It is probably easier to just upgrade to MSVC2015, or don't rely on overloading.

    Note that this technique is actually viable if you are overloading on return types, not on argument types. This is hard, because even MSVC2015 doesn't have "expression SFINAE", which cripples this kind of metaprogramming.