重载函数内部的函数重载
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 包装器)。
我正在使用 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 包装器)。