1

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?

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Viktor
  • 1,004
  • 1
  • 10
  • 16
  • 1
    I mean, you can abuse templates for this, but in your example, 1) you don't need overloads and 2) instead of `std::for_each` use a ranged for loop. – Rakete1111 Jul 30 '17 at 10:38
  • It is just an syntetic example. You can face with it in any context when use any STL algoritms or any functions with callables as input parameters. This is my point. – Viktor Jul 30 '17 at 10:40
  • [Have a look here](https://stackoverflow.com/questions/45187335/convert-template-function-to-generic-lambda/45187436#45187436). The very same problem occurs with function templates. The solution by Vittorio Romeo is general and neat, despite the use of macros. It will apply to your case as well (In fact, it even uses the same example for printing). – StoryTeller - Unslander Monica Jul 30 '17 at 10:48
  • Thanks. Makes sence. – Viktor Jul 30 '17 at 10:53

2 Answers2

3

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());
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • That will work for "Print". But I think the OP wants a more general solution. – StoryTeller - Unslander Monica Jul 30 '17 at 10:45
  • Asking out of curiosity: any particular reason to use a parameter pack (`args`) rather than a single parameter in the `print_wrapper()` lambda (given that the `Print` target is a single-parameter function)? Or just for generality in case `Print` overloads were to be expanded? – dfrib Jul 30 '17 at 10:47
  • Thanks! Good solution for `Print`. Drawback is I should write wrapper like this for every potentially overloaded function. – Viktor Jul 30 '17 at 10:51
  • @Viktor the trick is to think of overloading in terms of variadic functor concepts first, and then provide overloads to satisfy the concept of the functor. What you're doing is saying, "I have a bag of parts - how can I make that into a concept?". A better way is to create a concept and ask, "What parts to I need to make this concept work". – Richard Hodges Jul 30 '17 at 11:22
  • @StoryTeller as per my comment to Viktor. If we're having to do this, the design is broken. The concept of `Print` should be a functor which can be specialised. – Richard Hodges Jul 30 '17 at 11:24
3

Alternatively, you can use lambda:

Function(42, [](int i) {Print(i);});.
Jarod42
  • 203,559
  • 14
  • 181
  • 302