在 if 块中调用函数时如何使用复制省略

How to use copy elision when function is called in if block

在以下代码段中,由于复制省略

A 没有移动也没有复制发生
struct A;
A function1();
A function2();

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

这很好,但是 a 在 if 块之外无法访问。在外部声明 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

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

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

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

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

一般来说placement newarbitrary[=29]中使用纯右值(“强制复制省略”,不是复制省略) =] 上下文(例如,带有语句或重用变量)。然后,您还有责任手动调用 析构函数 ;安全和干净的方法是写一个助手 class:

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 包装在函数中当然会 materialize从任何函数返回的 A 的临时值。 (让 box::p 成为一个指针而不仅仅是一个标志可以避免使用 std::launder 来处理 lifetime 问题。)

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

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