【问题标题】:How to use copy elision when function is called in if block在 if 块中调用函数时如何使用复制省略
【发布时间】:2020-10-24 04:51:04
【问题描述】:

在下面的 sn-p 中,由于复制省略,A 没有移动也没有副本发生

struct A;
A function1();
A function2();

int main(int argc, char**) {
  if (argc > 3) {
    A a = function1();
  } else {
    A a = function2();
  }
  return 0;
}

这很好,但是在 if 块之外无法访问 a。当在外面声明a时,就会发生一个动作

struct A;
A function1();
A function2();

int main(int argc, char**) {
  A a;
  if (argc > 3) {
    a = function1();
  } else {
    a = function2();
  }
  return 0;
}

当复制省略应该发生在调用站点的 if 块中到 if 范围之外的变量中时,什么是值得推荐的从复制省略中获利的方法?

Compiler-Exlorer link

【问题讨论】:

    标签: c++ conditional-statements c++17 copy-elision


    【解决方案1】:

    在这种特殊情况下,您可以使用三元条件:

    A a = argc>3 ? function1() : function2();
    

    在更复杂的情况下,您可能需要保存条件并进行多次检查,例如

    const bool cond = argc>3;
    A a = cond ? function1() : function2();
    A b = cond ? function3() : function4();
    

    【讨论】:

      【解决方案2】:

      一般来说,您必须使用 placement new 来在任意上下文中使用纯右值(“强制复制省略”,而不是复制省略)( ,带有语句或变量的重用)。然后,您还有责任手动调用 析构函数;安全且干净的方法是编写一个辅助类:

      template<class T>
      struct box {
        char buf[sizeof(T)];  // real code should handle alignment
        T *p{};  // will point to buf
        void reset() {
          if(p) p->~T();
          p=nullptr;
        }
        ~box() {reset();}
      };
      
      void f() {
        box<A> a;
        if(…) a.p=new (a.buf) A(function1());
        else a.p=new (a.buf) A(function2());
        // use *a.p
      }
      

      当然,box 只是对 std::optional 的重新实现,只是暴露了胆量。不幸的是,您必须在外部分配给box::p,但是将new包装在一个函数中当然会实现一个临时的A从任何返回功能。 (让box::p 成为一个指针而不仅仅是一个标志可以避免使用std::launder 来处理生命周期 问题。)

      【讨论】:

      • 如果您要提倡安置new,您可能需要添加有关清理的说明(即手动调用 dtor)。
      • @StephenNewell:我做得比这更好:我为box 提供了一个析构函数,这样做是因为我无法忍受手动编写异常不安全的代码(或丑陋的@987654331 @)。
      • 是的,但我认为解释为什么您手动调用 dtor 很有用。我发现这是一个主要的混淆点,因为大量 C++ 开发人员没有使用 Placement new,并且认为手动调用 dtor 永远不正确。
      • @StephenNewell:很公平:我会在一些警告中进行编辑。
      【解决方案3】:

      立即调用的 Lambda 表达式 (IILE) 可以在这种情况和更复杂的情况下节省时间:

      A a = [&] {
        if (…) {
          return function1();
        } else {
          return function2();
        }
      }();
      

      【讨论】:

      猜你喜欢
      • 2014-10-03
      • 2018-07-30
      • 1970-01-01
      • 2021-10-26
      • 1970-01-01
      • 1970-01-01
      • 2012-11-19
      • 2016-11-20
      • 2021-12-16
      相关资源
      最近更新 更多