如何处理(深度)嵌套函数调用中的默认值?
How to deal with default values in (deeply) nested function calls?
假设有一个函数有默认值:
int foo(int x=42);
如果被别人这样调用:
int bar(int x=42) { return foo(x); }
int moo(int x=42) { return bar(x); }
这当然只是一个人为的例子。但是,我有时也会遇到类似的情况。该参数只是从最高级别 (moo
) 传递到最低级别,并且只有在那里才实际使用。这样做的坏处是,当我将 foo
更改为具有不同于 42
的默认值时,我将不得不搜索所有呼叫者并相应地更改默认值。
有什么pattern/idiom可以避免这种情况吗?
我想到的唯一简单的解决方案是
int bar() { return foo(); }
int bar(int x) { return foo(x); }
但是,由于我有点懒惰,而且在实际代码中这会导致相当多的代码重复,所以我想避免这种情况。
我建议 select 以下两个选项之一(正如您在其他答案中看到的那样 - 有更多可能的解决方案)。
- 超载你的函数
- 定义常量
因此,选项 1 如下所示:
int foo(int x=42);
int bar(int x) { return foo(x); }
int moo(int x) { return bar(x); }
int bar() { return foo(); }
int moo() { return bar(); }
而且,选项 2 会更短一些:
constexpr int FOO_DEFAULT = 42;
int foo(int x=FOO_DEFAULT);
int bar(int x=FOO_DEFAULT) { return foo(x); }
int moo(int x=FOO_DEFAULT) { return bar(x); }
对于默认值数量较少的情况(例如一个默认值),我会使用选项 1,对于您有很多默认值的情况,我会使用选项 2 - 例如 foo(int a, int b = 3, std::string c = "wow", float pi = 3.14)
您可以通过以下方式避免重复:
template<typename... T>
auto bar(T&&... t) { return foo(std::forward<T>(t)...); }
但这不是改进恕我直言。只是停止懒惰并定义在没有提供参数时调用 foo()
的重载。
实用的通用解决方案包括:
使用 Optional_
class 作为参数,例如 boost::optional
或 DIY 等价物。
命名默认值(并在包装函数定义中使用该名称)。
重载每个包装函数,如您在问题中所示。
只是在包装函数定义中重复默认值,但这违反了DRY原则,不要重复自己.
在 中,Tobi 提出了包装器 asdf
定义为
的情况
int asdf(int x=42,int y=42){ return foo(x)+foo(y);}
使用 Optional_
class:
auto foo( Optional_<int> x)
-> int
{ return (x.is_empty()? 42 : x.value()); }
auto asdf( Optional_<int> x = {}, Optional_<int> y = {} )
-> int
{ return foo( x ) + foo( y ); }
使用命名的默认值:
int const foo_default = 42;
auto foo( int x = foo_default )
-> int
{ return x; }
auto asdf( int x = foo_default, int y = foo_default )
-> int
{ return foo( x ) + foo( y ); }
使用重载:
auto foo( int x = 42 )
-> int
{ return x; }
auto asdf()
-> int
{ return foo() + foo(); }
auto asdf( int x )
-> int
{ return foo( x ) + foo(); }
auto asdf( int x, int y )
-> int
{ return foo( x ) + foo( y ); }
值得注意的是,asdf
不能简单地定义为转发其参数的函数模板。此外,这样的模板不能轻易地在单独的翻译单元中定义,也不能获取其地址。由于这些原因,我没有在项目符号列表中包括这个可能的解决方案:它非常受限,不是通用解决方案。
假设有一个函数有默认值:
int foo(int x=42);
如果被别人这样调用:
int bar(int x=42) { return foo(x); }
int moo(int x=42) { return bar(x); }
这当然只是一个人为的例子。但是,我有时也会遇到类似的情况。该参数只是从最高级别 (moo
) 传递到最低级别,并且只有在那里才实际使用。这样做的坏处是,当我将 foo
更改为具有不同于 42
的默认值时,我将不得不搜索所有呼叫者并相应地更改默认值。
有什么pattern/idiom可以避免这种情况吗?
我想到的唯一简单的解决方案是
int bar() { return foo(); }
int bar(int x) { return foo(x); }
但是,由于我有点懒惰,而且在实际代码中这会导致相当多的代码重复,所以我想避免这种情况。
我建议 select 以下两个选项之一(正如您在其他答案中看到的那样 - 有更多可能的解决方案)。
- 超载你的函数
- 定义常量
因此,选项 1 如下所示:
int foo(int x=42);
int bar(int x) { return foo(x); }
int moo(int x) { return bar(x); }
int bar() { return foo(); }
int moo() { return bar(); }
而且,选项 2 会更短一些:
constexpr int FOO_DEFAULT = 42;
int foo(int x=FOO_DEFAULT);
int bar(int x=FOO_DEFAULT) { return foo(x); }
int moo(int x=FOO_DEFAULT) { return bar(x); }
对于默认值数量较少的情况(例如一个默认值),我会使用选项 1,对于您有很多默认值的情况,我会使用选项 2 - 例如 foo(int a, int b = 3, std::string c = "wow", float pi = 3.14)
您可以通过以下方式避免重复:
template<typename... T>
auto bar(T&&... t) { return foo(std::forward<T>(t)...); }
但这不是改进恕我直言。只是停止懒惰并定义在没有提供参数时调用 foo()
的重载。
实用的通用解决方案包括:
使用
Optional_
class 作为参数,例如boost::optional
或 DIY 等价物。命名默认值(并在包装函数定义中使用该名称)。
重载每个包装函数,如您在问题中所示。
只是在包装函数定义中重复默认值,但这违反了DRY原则,不要重复自己.
在 asdf
定义为
int asdf(int x=42,int y=42){ return foo(x)+foo(y);}
使用 Optional_
class:
auto foo( Optional_<int> x)
-> int
{ return (x.is_empty()? 42 : x.value()); }
auto asdf( Optional_<int> x = {}, Optional_<int> y = {} )
-> int
{ return foo( x ) + foo( y ); }
使用命名的默认值:
int const foo_default = 42;
auto foo( int x = foo_default )
-> int
{ return x; }
auto asdf( int x = foo_default, int y = foo_default )
-> int
{ return foo( x ) + foo( y ); }
使用重载:
auto foo( int x = 42 )
-> int
{ return x; }
auto asdf()
-> int
{ return foo() + foo(); }
auto asdf( int x )
-> int
{ return foo( x ) + foo(); }
auto asdf( int x, int y )
-> int
{ return foo( x ) + foo( y ); }
值得注意的是,asdf
不能简单地定义为转发其参数的函数模板。此外,这样的模板不能轻易地在单独的翻译单元中定义,也不能获取其地址。由于这些原因,我没有在项目符号列表中包括这个可能的解决方案:它非常受限,不是通用解决方案。