【问题标题】:C++ design : functions with boolean optionsC++ 设计:带有布尔选项的函数
【发布时间】:2012-10-03 19:17:00
【问题描述】:

我有一个关于 C++ 中“良好设计实践”的问题。我正在用 C++11 编写一个数值库,并且我使用了大量的元编程和基于模板的技术。但我有一个非常基本的问题:

考虑一个可以有两个非常接近的行为的函数,除了一个可以由布尔标志激活的选项。我只考虑开发人员可以设置/取消设置的标志,而不考虑可以在运行时设置/取消设置的标志。有3种设计可能性:


1) 编写两个名称中带有显式选项的函数:

myFunctionFlag1(...);
myFunctionFlag2(...); 

2) 使用模板参数:

template<bool Flag> myFunction(...);

3) 使用可变参数:

myFunction(..., const bool flag);

就良好的设计实践而言,哪种解决方案可以接受/不可接受?如果有最好的解决方案,它是哪一个,为什么?如果有最坏的解决方案,是哪一个?为什么?

编辑:对于所考虑的函数,运行时开销可以忽略不计,因此这不是最关键的点。

编辑 2:我知道这三个都有效。但由于我的图书馆会有用户,它需要有一个可靠/良好的设计。

选项 2 是否常见(因为在我看来这是一个很好的折衷方案)?

【问题讨论】:

标签: c++ boolean flags


【解决方案1】:

以上都不是。布尔标志对代码的可读性很不利。如果标志控制的功能足够小以至于使用单个函数有意义,则使用单个函数,但不要使用 bool 作为标志的类型。相反,使用具有有用名称的枚举器的枚举:

enum class MyFunctionMode { EnableFoo, DisableFoo };

void myFunction(..., MyFunctionMode mode);

这种模式使调用站点很容易理解为函数提供了哪些选项。可以使用标志组合多个选项。

【讨论】:

  • 布尔参数不清楚和new Rectangle(50, 50, 100, 100)之类的参数有什么区别?是的,我们可以使用常量来删除幻数,但是责任在于 Rectangle 的用户,而 bools 的枚举解决方案是由类本身强制执行的。 Rectangle 构造函数是否应该采用 Point 和 Dimension(或 Point 和 Point)类型的参数?或者也许是 typedef 参数?
  • @sftrabbit:是的,我认为Rectangle 构造函数应该采用一对Points,或类似的东西。这样做会使代码更容易理解,并且没有运行时开销(至少对于一个好的、现代的优化编译器来说不是)。此外,最好使用类型对尽可能多的信息进行编码(例如,您有 两个二维点,而不是 四个整数)。类型错误是在编译时发现的,编译错误比任何类型的运行时错误都要好得多,因为它们不会被忽视。
【解决方案2】:

我的建议是使用选项 1。

评论:

  1. 最简单明了。它没有运行时开销。
  2. 我没有看到足够的理由(如果提供更多细节,可能仍然存在)使用这种方法。它比选项 1 更复杂。
  3. 此变体具有运行时开销。它仍然可以在非时间/CPU 关键代码中使用。如果你的函数很大(超过 100 行),那么这个变体会变得更有吸引力,

我的 2 美分。

【讨论】:

    【解决方案3】:

    您的第一个解决方案重复了代码,这是没有人愿意做的。

    在第三个中是可以接受的,但 if 语句将在运行时进行评估,这意味着浪费时间。

    模板一似乎是合适的。 if 语句在编译时会是 if (true)if (false),编译器会对其进行优化,就好像它们不是 if 语句一样。

    【讨论】:

    • “您的第一个解决方案重复了代码” - 除非您根据另一个实现一个。
    【解决方案4】:

    你知道的三个都可以。

    void fun_that_does_stuff();
    void fun_that_does_something_else();
    
    template < bool F >
    void fun_that_does_stuff_or_something();
    template < >
    void fun_that_does_stuff_or_something<true>() { fun_that_does_stuff(); }
    
    ... blah blah.
    

    【讨论】:

      【解决方案5】:

      复制重要的代码是最糟糕的选择。如果这两个函数只是分派给实际有趣的函数,那么它可能是正确的选择。当然,这两个函数可以分派到模板版本,但它们可以比truefalse 更好地指示差异。

      另一个问题是函数的实际使用方式:如果它们本身是从具有相同配置或可以派生标志选择的配置的函数调用的,则使用不同的名称会变得相当烦人甚至不可维护:你会最终不得不有条件地调用函数:

      (flag? &myFunctionFlag1: &myFunctionFlag2)(...);
      

      其他两个版本之间的选择归结为一个问题:支票重要吗?如果是这样,则模板是唯一的选择。如果不是,您可以选择任何一个版本,我敢打赌,匿名投票会投票支持运行时版本:当我向他们展示一个微不足道的模板时,我的大多数同事的眼睛都会眨眨眼。

      【讨论】:

        猜你喜欢
        • 2011-06-04
        • 1970-01-01
        • 2019-06-26
        • 2020-04-14
        • 2011-08-23
        • 1970-01-01
        • 2015-05-23
        • 1970-01-01
        • 2020-06-10
        相关资源
        最近更新 更多