重载函数内部的函数重载

Function overloading inside overloaded function

我正在使用 C++14(而且还很新)。我有 3 个重载函数 func,其中另一个重载函数 do_something 根据其父函数 (func) 被调用。

int func(int a) {
   bar(a);
   foo();
 }

int func(int a, float b) {
   bar(a);
   do_something(b);
   foo();
}

int func(int a, float b, char c) {
   bar(a);
   do_something(b, c);
   foo();
 }

我看到 func 中的功能几乎相同,只是调用了 do_something 的哪个版本。有什么方法可以使这个通用并将所有 func 组合在一起?

第一步是使用 variadic-templates 将您要转发的部分转发给 do_something:

template<class ... Args>
int func(int a, Args... args)
{
   bar(a);
   do_something(std::forward<Args>(args)...)
   foo();
}

但是现在你丢失了函数的参数类型。所以如果这是一个问题,你将不得不找到一种方法来再次测试它们。

首先,制作 func 接受参数包的模板。 int a 参数、对 bar 的调用和对 foo 的调用始终存在,所以这很简单。现在让我们为 do_something 添加一个占位符。

template <class ...Args>
int func(int a, Args&&... other)
{
   bar(a);
   // somehow call do_something and do the right thing
   foo();

   return 0;
}

您想像以前一样实例化和调用上述模板:

func(42);
func(42, 1.f);
func(42, 1.f, 'A');

现在让我们来处理对 do_something 的调用。如果您只是将它添加到新 func 模板的中间;

do_something(std::forward<Args>(other)...);

这拒绝为 func(42) 编译,即只有一个参数的情况。因此,我们需要一个特例。在 do_something:

的另一个间接级别中实现此目的的一种方法
// No additional argument case, does nothing:
void do_something_wrapper() {}

// The other two cases    
template <class ...Args>
void do_something_wrapper(Args&&... args)
{
   do_something(std::forward<Args>(args)...);
}

现在,func 函数模板中的占位符应该是:

do_something_wrapper(std::forward<Args>(other)...);

虽然我自己可能会接受 generic_opto_guy 的回答,但他指出您会丢失界面中的类型是正确的。根据您的情况,您可能希望保留此内容。

在这种情况下,您可以轻松地将其修改为类似于以下内容的内容:

namespace details {

template<class ... Args>
int func_impl(int a, Args &&... args)
{
   bar(a);
   do_something(std::forward<Args>(args)...)
   foo();
}

}

int func(int a) { return details::func_impl(a); }

int func(int a, float b) { return details::func_impl(a, b); }

int func(int a, float b, char c) { return details::func_impl(a, b, c); }

请注意,实施已调整为使用 perfect forwarding。虽然在这种特殊情况下不需要,但它在您将来可能遇到的转发情况下通常很有用。

同样,除非您绝对需要为客户端代码提供清晰的接口,否则我会选择第一个实现。

其他答案是对所问问题的良好通用答案,但如果您有几个 "spare" 值 c 可用,您可以这样写:

int func(int a) {
   func(a, 0.0,'[=10=]1');
}

int func(int a, float b, char c = '[=10=]2') {
   bar(a);
   switch (c) {
   case '[=10=]1':
      break;              // func(a)
   case '[=10=]2':
      do_something(b):    // func(a, b)
      break;
   default:
      do_something(b, c); // func(a, b, c)
      break;
   }
   foo();
}

根据应用程序,这可能更简单。

来不及玩了?

你标记了 C++14,但是,如果你可以使用 C++17,你可以使用 if constexpr 所以

template <typename ... Ts>
int func (int a, Ts && ... ts)
 {
   bar(a);

   if constexpr ( sizeof...(Ts) > 0u )
      do_something(std::forward<Ts>(ts)...);

   foo();

   // and remember to return something of int
 }

在 C++14 中,您必须在某处复制一些内容。

或者你写两个func()s

int func (int a)
 {
   bar(a);    
   foo();

   // and remember to return something of int
 }


template <typename ... Ts>
int func (int a, Ts && ... ts)
 {
   bar(a);

   do_something(std::forward<Ts>(ts)...);

   foo();

   // and remember to return something of int
 }

或者你添加一个no-argumentdo_something()

void do_something ()
 { }

或者,按照 lubgr 的建议,您通过包装器调用 do_something()(创建一个特殊的 no-argument 包装器)。