CppCoreGuidelines I.23 函数参数的数量不要太多
28 March 2022
理由:
参数的数量太多容易混乱出错。传递太多参数,成本也通常会很高。
讨论:
导致函数的参数太多,通常有两个理由:
- 缺少抽象。因为缺少抽象,本该组合的对象被分散为单个传递。因为没有一个不变的框架来约束参数的数值,不仅参数的数量变大,也容易出错。
- 违背了“一个函数一个职责”的原则。函数的工作超越了其本该完成的单个任务。
例子:
标准库函数 merge()
是我们能舒服处理的极限:
template<class InputIterator1, class InputIterator2, class OutputIterator, class Compare> OutputIterator merge(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp);
因为“缺少抽象”,本该是传进一个范围(抽象),STL 却传进了迭代器对(未封装的组件值)。
这里,我们有4个模板参数,6个函数参数。如果我们默认比较器参数为 “<”,则可以改写成下面代码:
template<class InputIterator1, class InputIterator2, class OutputIterator> OutputIterator merge(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result);
降低了一些表面的复杂度,但是没有降低整体复杂度。要真正减少参数数量,我们需要把参数捆绑到更高层的抽象:
template<class InputRange1, class InputRange2, class OutputIterator> OutputIterator merge(InputRange1 r1, InputRange2 r2, OutputIterator result);
把参数分组打包是一种常用的减少参数数量的技术。并且还可以提升可检查性。
另外,我们也可以用 ISO TS 中的定义概念,指定必须有三个类型用于合并:
Mergeable{In1, In2, Out} OutputIterator merge(In1 r1, In2 r2, Out result);
例子:
安全配置建议将以下函数
void f(int* some_ints, int some_ints_length); // BAD: C style, unsafe
改为
void f(gsl::span<int> some_ints); // GOOD: safe, bounds-checked
此处使用抽象,更安全,更可靠,同时减少了参数的数量。
注意:
多少参数算太多?尽量少于 4 个参数。尽管有的函数需要用四个参数很好的表达,但是不多。
替代方案:用更好的抽象。把参数分组到有意义的对象,(以值或引用)传递对象。
替代方案:用默认参数或重载。这样常用的调用要少一些参数。
强化:
- 函数声明了两个类型一样的迭代器或指针,而没用
range
或view
,警告。