什么是复制省略以及它如何优化复制和交换习惯用法?

发布于 2024-08-19 10:48:16 字数 402 浏览 1 评论 0原文

我正在阅读复制和交换

我尝试阅读有关 Copy Elision 的一些链接,但无法正确理解它的含义。有人可以解释一下这个优化是什么,特别是以下文本的含义吗

这不仅仅是一个方便的问题,而且实际上是一种优化。如果参数绑定到左值(另一个非常量对象),则在创建参数时会自动创建该对象的副本。但是,当 s 绑定到右值(临时对象、文字)时,通常会忽略副本,从而节省对复制构造函数和析构函数的调用。在早期版本的赋值运算符中,参数被接受为 const 引用,当引用绑定到右值时,不会发生复制省略。这会导致创建和销毁另一个对象。

I was reading Copy and Swap.

I tried reading some links on Copy Elision but could not figure out properly what it meant. Can somebody please explain what this optimization is, and especially what is mean by the following text

This is not just a matter of convenience but in fact an optimization. If the parameter (s) binds to a lvalue (another non-const object), a copy of the object is made automatically while creating the parameter (s). However, when s binds to a rvalue (temporary object, literal), the copy is typically elided, which saves a call to a copy constructor and a destructor. In the earlier version of the assignment operator where the parameter is accepted as const reference, copy elision does not happen when the reference binds to a rvalue. This results into an additional object being created and destroyed.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

泪意 2024-08-26 10:48:16

复制构造函数的存在是为了进行复制。理论上,当您编写如下行时:

CLASS c(foo());

编译器必须调用复制构造函数将 foo() 的返回值复制到 c 中。

复制省略是一种跳过调用复制构造函数以避免支付开销的技术。

例如,编译器可以安排foo()将其返回值直接构造到c中。

这是另一个例子。假设你有一个函数:

void doit(CLASS c);

如果你用实际参数调用它,编译器必须调用复制构造函数,以便原始参数不能被修改:

CLASS c1;
doit(c1);

但现在考虑一个不同的例子,假设你像这样调用你的函数:

doit(c1 + c1);

< code>operator+ 必须创建一个临时对象(右值)。编译器可以传递由 operator+ 创建的临时对象并将其传递给 doit(),而不是在调用 doit() 之前调用复制构造函数> 相反。

The copy constructor exists to make copies. In theory when you write a line like:

CLASS c(foo());

The compiler would have to call the copy constructor to copy the return of foo() into c.

Copy elision is a technique to skip calling the copy constructor so as not to pay for the overhead.

For example, the compiler can arrange that foo() will directly construct its return value into c.

Here's another example. Let's say you have a function:

void doit(CLASS c);

If you call it with an actual argument, the compiler has to invoke the copy constructor so that the original parameter cannot be modified:

CLASS c1;
doit(c1);

But now consider a different example, let's say you call your function like this:

doit(c1 + c1);

operator+ is going to have to create a temporary object (an rvalue). Instead of invoking the copy constructor before calling doit(), the compiler can pass the temporary that was created by operator+ and pass that to doit() instead.

若无相欠,怎会相见 2024-08-26 10:48:16

下面是一个示例:

#include <vector>
#include <climits>

class BigCounter {
 public:
   BigCounter &operator =(BigCounter b) {
      swap(b);
      return *this;
   }

   BigCounter next() const;

   void swap(BigCounter &b) {
      vals_.swap(b);
   }

 private:
   typedef ::std::vector<unsigned int> valvec_t;
   valvec_t vals_;
};

BigCounter BigCounter::next() const
{
   BigCounter newcounter(*this);
   unsigned int carry = 1;
   for (valvec_t::iterator i = newcounter.vals_.begin();
        carry > 0 && i != newcounter.vals_.end();
        ++i)
   {
      if (*i <= (UINT_MAX - carry)) {
         *i += carry;
      } else {
         *i += carry;
         carry = 1;
      }
   }
   if (carry > 0) {
      newcounter.vals_.push_back(carry);
   }
   return newcounter;
}

void someFunction()
{
    BigCounter loopcount;
    while (true) {
       loopcount = loopcount.next();
    }
}

somefunction 中,loopcount = loopcount.next(); 行从复制省略中受益匪浅。如果不允许复制省略,则该行将需要 3 次复制构造函数调用以及对析构函数的关联调用。在允许复制省略的情况下,可以减少对复制构造函数的 1 次调用,即声明 newcounterBigCount::next() 内部的显式调用。

如果 operator = 是这样声明和定义的:

BigCounter &BigCounter::operator =(const BigCounter &b) {
   BigCounter tmp(b);
   swap(tmp);
   return *this;
}

即使使用复制省略,也必须调用两次复制构造函数。一个用于构造 newcounter,另一个用于构造 tmp。如果没有复制省略,仍然会有 3 个。这就是为什么声明 operator = 以便其参数需要调用复制构造,这在使用赋值运算符的“复制和交换”习惯用法时可以是一种优化。当调用复制构造函数来构造参数时,它的调用可能会被省略,但如果调用它来创建局部变量,则可能不会。

Here is an example:

#include <vector>
#include <climits>

class BigCounter {
 public:
   BigCounter &operator =(BigCounter b) {
      swap(b);
      return *this;
   }

   BigCounter next() const;

   void swap(BigCounter &b) {
      vals_.swap(b);
   }

 private:
   typedef ::std::vector<unsigned int> valvec_t;
   valvec_t vals_;
};

BigCounter BigCounter::next() const
{
   BigCounter newcounter(*this);
   unsigned int carry = 1;
   for (valvec_t::iterator i = newcounter.vals_.begin();
        carry > 0 && i != newcounter.vals_.end();
        ++i)
   {
      if (*i <= (UINT_MAX - carry)) {
         *i += carry;
      } else {
         *i += carry;
         carry = 1;
      }
   }
   if (carry > 0) {
      newcounter.vals_.push_back(carry);
   }
   return newcounter;
}

void someFunction()
{
    BigCounter loopcount;
    while (true) {
       loopcount = loopcount.next();
    }
}

In somefunction the line loopcount = loopcount.next(); benefits greatly from copy elision. If copy elision were not allowed, that line would require 3 invocations of the copy constructor and an associated call to a destructor. With copy elision being allowed, it can be reduced to 1 call of the copy constructor, the explicit one inside of BigCount::next() where newcounter is declared.

If operator = had been declared and defined like this:

BigCounter &BigCounter::operator =(const BigCounter &b) {
   BigCounter tmp(b);
   swap(tmp);
   return *this;
}

there would've had to have been 2 invocations of the copy constructor, even with copy elision. One to construct newcounter and the other to construct tmp. And without copy elision there would still be 3. That's why declaring operator = so its argument requires invoking the copy construct can be an optimization when using the 'copy and swap' idiom for the assignment operator. When the copy constructor is invoked for constructing an argument, its invocation may be elided, but if it's invoked to create a local variable, it may not be.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文