【问题标题】:Conditional initialization of a const variableconst 变量的条件初始化
【发布时间】:2017-09-21 00:02:16
【问题描述】:

以下基本代码是一个相当大的过程的一部分:

int x = foo();
if (x == 0) x = bar();

x 没有在其他任何地方修改,所以我可以这样做:

const int x = foo() == 0 ? bar() : foo();

但是foo() 是一个非常昂贵且复杂的函数,因此我不能调用它两次,因为它可能会产生竞争条件并因此获得不同的值(它可能涉及读取外部资源)。

我想让代码尽可能可读,并且尽可能简短。一种选择是:

const int foo_ = foo(), x = foo_ == 0 ? bar() : foo_;

另一方面,我想避免这种时间变量,主要是因为foo() 可能依赖于外部资源,所以在其余代码中使用foo_ 作为缓存值是无效的。

我正在发布我现在正在使用的解决方案,但我想知道是否有更好的选择(没有或很少有代码混乱,同一范围内没有时间变量,可读性......)。提前致谢!

PS:它必须至少遵循 C++11 标准,因为它属于跨平台项目。

我知道这可能是基于意见的,但鉴于前面关于简单性(不是混乱代码)和避免时间变量(不是为了可读性而是为了代码安全)的陈述,我想知道解决这个问题的选项。

【问题讨论】:

  • 三元运算符在您编写时可能看起来更好,但是当您在一个月后回到代码时,它们是一个难以破译的噩梦,尤其是如果您不是自己编写的。短代码的可读性并不总是很好,添加 cmets 可能是为了
  • 请解释为什么您认为“临时”变量会阻碍“代码安全”,尤其是比聪明、难以理解的“技巧”更严重?
  • 我同意时间变量更容易理解,我只是想避免由于此类变量的存在而使用无效状态的机会(缓存foo() 的返回值)。这就是我问这个问题的原因,是否有机会兼得两全:可读性和安全性?
  • 如果您的程序“相当大”,您可能应该首先专注于将其拆分为更小的程序(如果您的目标是可维护性......)
  • @chtz 当然,这也是我们现在的目标之一!

标签: c++ c++11 initialization constants


【解决方案1】:

一个简单的呢:

const int temp_foo = foo();
const int x = (temp_foo == 0) ? bar() : temp_foo;

【讨论】:

  • Tahnks,但是很抱歉,正如问题中提到的,我想避免临时变量。
  • @cbuchart :人们会期望它会被优化掉,而且我发现它比 lambda 更容易混淆。
  • @cbuchart: “我想避免 [临时] 变量” 为什么?这是最明显、最实用、最正常的解决方案。您的编译器和其他人都可以理解。它应该绝对没有运行时损失(为什么会呢?你有相同数量的数据和对象)
  • 鉴于foo()的性质,如果它的结果​​被缓存在一个时间变量中,并且该变量稍后被使用,它可能代表一个无效状态。我想避免这种情况。
【解决方案2】:

基本上,您想要Elvis operator,但 C++ 没有。这是您可能想要第二次使用的那种东西。因此,我选择了通用解决方案,而不是您提出的 lambda 解决方案。

例如

#include <functional>

int func_elvis (std::function<int ()> func1, std::function<int ()> func2) {
  int tmp = func1();
  return tmp ? tmp : func2();
}

然后像这样使用

const int x = func_elvis(foo, bar);

如果你喜欢自己调用函数,你可以这样做

#define ELVIS(A, B) func_elvis([](){ return A; }, [](){ return B; })

并像使用它

const int x = ELVIS(foo(), bar());

【讨论】:

    【解决方案3】:

    到目前为止我发现的解决方案是使用 lambda 函数,例如:

    const int x = [](int n) { return n == 0 ? bar() : n; }(foo());
    

    【讨论】:

    • 是的,但正如问题中所述,我对时间变量的关注是由于foo() 的性质而不是时间变量本身(如果使用时间变量,它的值可能会超出日期)。另一方面,如果不是int,我将使用具有复杂复制构造函数的类型,那么我将使用const &amp; 更改它。
    • 这太复杂了,不容易理解。
    【解决方案4】:

    由于这是基于意见的问题,我会选择:

    auto get_the_correct_x=[](){
        const auto temp=foo();
        return temp==0?bar():temp;
    }
    const auto x=get_the_correct_x();
    

    一个好的名字而不是get_the_correct_x 会比仅仅尝试在没有命名的情况下使用 lambdas 更清晰。

    【讨论】:

    • 如果你经常使用它,我建议把它变成一个合适的方法/函数,而不是一个 lambda 表达式。
    • 到目前为止,这是我觉得更有用的解决方案,可能比内联 lambda 更大,但生成的代码更具可读性,谢谢!我将使用我使用的最终实现来更新问题
    【解决方案5】:

    除了 lambda,如果这种情况在您的程序中不会多次出现,您可以将函数 foo() 包装成一个函数,比如 conditionalFoo()

    int conditionalFoo() {
      int result = foo();
      if (result==0)
        result = bar();
      return result;
    }
    ...
    const int x = conditionalFoo();
    

    【讨论】:

    • 你可以在你的函数中使用return bar();
    • 另外,您可以将foo() 作为参数传递(使其更类似于OP 提出的lambda-expression 解决方案)。那么该方法应该命名为int conditionalBar(int foo)
    【解决方案6】:

    如果你乐于使用gcc extensions,那么你可以写:

    const int x = foo() ?: bar();
    

    【讨论】:

    • 看起来不错,但不幸的是我必须遵循标准,因为它是跨平台代码。我会考虑 gcc 代码,谢谢!很高兴在未来的标准中看到它。
    猜你喜欢
    • 1970-01-01
    • 2017-05-01
    • 1970-01-01
    • 2014-09-08
    • 2021-10-19
    • 1970-01-01
    • 1970-01-01
    • 2019-09-02
    • 2016-08-14
    相关资源
    最近更新 更多