This is another case of trying to get rid of a macro. Consider
void f_(int i) { printf("f_(int)\t%d\n", i); }
void f_(int i, double x) { printf("f_(int, double)\t%d, %5.3f\n", i, x); }
void g_(int i) { printf("g_(int)\t%d\n", i); }
void g_(int i, double x) { printf("g_(int, double)\t%d, %5.3f\n", i, x); }
(Imagine f_()
gets data form a .foo file or uses hard-coded "dummy" values, g_()
does the same for .bar .) There might be a function to decide which overload to call:
void f(int i, double d) { (i > 0) ? f_(i, d) : f_(i); }
with the same logic duplicated for g_()
:
void g(int i, double x) { (i > 0) ? g_(i, x) : g_(i); }
Getting rid of that duplicated code is easy with a macro:
#define h(i, x, func_) (i > 0) ? func_(i, x) : func_(i);
// ...
h(-1, 314.1, f_);
h(99, 314.1, f_);
h(-1, 314.1, g_);
h(99, 314.1, g_);
But of course we'd rather not use macros in C++. The "obvious" template
template<typename T>
void h2(int i, double x, T t)
{
(i > 0) ? t(i, x) : t(i);
}
// ...
h2(-1, 314.1, f_);
fails because the compiler can't figure out what overload of f_()
to use.
How can I replace the functionality of the macro h
?
You can use a variadic lambda and have the lambda call the function you wish to call.
template<typename T>
void h2(int i, double x, T t)
{
i > 0 ? t(i, x) : t(i);
}
int main()
{
h2(-1, 314.1, [](auto... args){ f_(args...); });
// ^^ change this to g_ if you want to use g_ instead of f_
}
If you don't mind changing the definition's of f_
and g_
, you can do something like
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
auto f_ = overloaded(
[](int i) { printf("f_(int)\t%d\n", i); },
[](int i, double x) { printf("f_(int, double)\t%d, %5.3f\n", i, x); }
);
auto g_ = overloaded(
[](int i) { printf("g_(int)\t%d\n", i); },
[](int i, double x) { printf("g_(int, double)\t%d, %5.3f\n", i, x); }
);
Then your h
template is exactly what you want
template<typename T>
void h(int i, double x, T t)
{
i > 0 ? t(i, x) : t(i);
}
The overloaded
template shamelessly copied from this example for std::visit
.
To make it work for C++11 , you have to adapt the overloaded
template and add a helper function to deduce the type parameters
template<class... Ts> struct overloads;
template<> struct overloads<>{};
template<class T, class... Ts> struct overloads<T, Ts...> : T, overloads<Ts...>
{
overloads(T t, Ts... ts) : T(t), overloads<Ts...>(ts...) {}
using T::operator();
};
template<class... Ts> overloads<Ts...> overloaded(Ts&&... ts)
{ return overloads<Ts...>(std::forward<Ts>(ts)...); }
You cannot do it directly, because the type infered for T
would need to include the parameter types, but you can do something along the line of
#include <iostream>
struct overloader {
void foo(int) { std::cout << "int\n";}
void foo(double) { std::cout << "double\n";}
};
template <typename T>
void bar(int x,double y,bool opt, T t){
if (opt) { t.foo(x); }
else { t.foo(y); }
}
int main() {
overloader ol;
bar(1,1,true,ol);
bar(1,1,false,ol);
}
prints:
int
double
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.