如何将多个函数重载作为单个参数传递?

Ðаn*_*Ðаn 8 c++ macros templates

这是试图摆脱宏的另一种情况.考虑

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); }
Run Code Online (Sandbox Code Playgroud)

(想象一下f_().foo文件中获取数据或使用硬编码的"虚拟"值,g_().bar执行相同操作.)可能有一个函数来决定调用哪个重载:

void f(int i, double d) { (i > 0) ? f_(i, d) : f_(i); }
Run Code Online (Sandbox Code Playgroud)

具有相同的逻辑重复g_():

void g(int i, double x) { (i > 0) ? g_(i, x) : g_(i); }
Run Code Online (Sandbox Code Playgroud)

使用宏可以轻松删除重复的代码:

#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_);
Run Code Online (Sandbox Code Playgroud)

但当然我们宁愿不在C++中使用宏."明显"的模板

template<typename T>
void h2(int i, double x, T t)
{
   (i > 0) ? t(i, x) : t(i);
}
// ...
h2(-1, 314.1, f_);
Run Code Online (Sandbox Code Playgroud)

失败,因为编译器无法弄清楚f_()要使用的重载.

如何替换宏的功能h

Nat*_*ica 7

您可以使用可变参数lambda并让lambda调用您想要调用的函数.

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_
}
Run Code Online (Sandbox Code Playgroud)


Cal*_*eth 6

如果你不介意改变定义的中f_g_,你可以这样做

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); }
);
Run Code Online (Sandbox Code Playgroud)

然后你的h模板正是你想要的

template<typename T>
void h(int i, double x, T t)
{
   i > 0 ? t(i, x) : t(i);
}
Run Code Online (Sandbox Code Playgroud)

overloaded模板无耻地从复制这个例子std::visit.

要使其适用于C++ 11,您必须调整overloaded模板并添加辅助函数以推断类型参数

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)...); }
Run Code Online (Sandbox Code Playgroud)

  • @Ð是一个技巧是制作单个对象而不是单个名称.我只记得`std :: variant`示例作为一种有用的模式 (2认同)
  • @rustyx我认为你可以做一个C++ 11变体,它只涉及一个`make_`函数而不是一个演绎指南 (2认同)