Search code examples
c++templatesstloverloading

How to solve function overloading ambiguity in following code


Suppose there are function overloads for int and double.

void Print(int v)
{
    cout << v << endl;
}
 
void Print(double v)
{
    cout << v << endl;
}
 

There is function which is intended to pass in as callable one of above functions.

template<typename FnT>
void Function(int v, FnT&& fn)
{
    fn(v);
}

But following code is ambiguous:

Function(1, Print);

Live example

Compiler fails to deduce type for second argument. This can be easily solved:

Function(1, static_cast<void(*)(int)>(Print));

I believe exists more generic and elegant way to solve the issue. The issue can be raised up in STL algorithm, when function overload is added.

vector<int> v = { 1,2,3 };
for_each(v.begin(), v.end(), Print); // ambiguous code

How to solve this in a pretty way?


Solution

  • Wrap it in a function object:

    #include <iostream>
    #include <utility>
    
    void Print(int v)
    {
        std::cout << v << std::endl;
    }
    
    void Print(double v)
    {
        std::cout << v << std::endl;
    }
    
    template<typename FnT>
    void Function(int v, FnT&& fn)
    {
        fn(v);
    }
    
    auto print_wrapper() {
        return [](auto&&...args) -> decltype(auto)
        {
            return Print(std::forward<decltype(args)>(args)...);
        };
    }
    
    int main()
    {
        Function(1, print_wrapper());
    }
    

    On a more theoretical note, what we're doing here is using a bandage to fix a broken design.

    A better design would be to define the concept of Print as a template function object. Then we can specialise it and customise it to our heart's content.

    A (very complete) model of this is boost::hash<> which is worth half a day of anyone's time as a study exercise.

    A simple example:

    #include <iostream>
    #include <utility>
    #include <string>
    
    struct Printer
    {
        void operator()(const int& i) const {
            std::cout << i << std::endl;
        }
    
        void operator()(const double& i) const {
            std::cout << i << std::endl;
        }
    
        template<class Anything>
        void operator()(const Anything& i) const {
            custom_print(i);
        }
    };
    
    struct Foo
    {
    
    };
    void custom_print(Foo const& f) 
    {
        std::cout << "a Foo" << std::endl;
    }
    
    template<typename X, typename FnT>
    void Function(X const& x, FnT&& fn)
    {
        fn(x);
    }
    
    int main()
    {
        Function(1, Printer());
        Function(1.0, Printer());
        Function(Foo(), Printer());
    }