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);
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?
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());
}