在 std::bitset::operator[] 中创建的 std::bitset::reference 对象的生命周期?

Lifetime of std::bitset::reference object created in std::bitset::operator[]?

我一直在查看 bitset 标准 C++ 库头文件的头文件。我发现重载的 operator[] operator[](size_t ndx)(在 class bitset 中定义)returns 是 class reference 的临时对象。

reference
    operator[](size_t __position)
{ return reference(*this,__position); }

这个重载运算符封装了单个位的概念。 class 的一个实例是实际位的代理。它在

这样的表达式中很有用
bitset<10> b;
b[2] = true;

reference class 定义了重载的 = 运算符成员函数,因此上面的例子可以工作:

 //For b[i] = __x;
 reference&
     operator=(bool __x)
 {
   if (__x)
     *_M_wp |= _Base::_S_maskbit(_M_bpos);
   else
     *_M_wp &= ~_Base::_S_maskbit(_M_bpos);
   return *this;
}

但是,我对这个表达式感到困惑:

if (b[2]) {
    //Do something
}

b[2]首先returns一个classreference的临时对象,然后在返回的临时对象上调用重载运算符(operator bool() const)将其转换为 bool 数据类型。

// For __x = b[i];
operator bool() const
{ return (*(_M_wp) & _Base::_S_maskbit(_M_bpos)) != 0; }

如果在堆栈上创建临时对象(具有自动存储的对象 class),则调用另一个函数 (operator bool() const) 不应破坏第一个函数调用返回的临时对象 ( reference reference operator[](size_t __position))?

返回的对象

C 和 C++ 中临时对象的生命周期是多少?

包括 operator bool() 在内的 if (b[2]) 中的条件是单个表达式,临时变量在表达式的整个生命周期内都有效。

来自class.temporary#4,强调的是我的

When an implementation introduces a temporary object of a class that has a non-trivial constructor ([class.ctor], [class.copy]), it shall ensure that a constructor is called for the temporary object. Similarly, the destructor shall be called for a temporary with a non-trivial destructor ([class.dtor]). Temporary objects are destroyed as the last step in evaluating the full-expression ([intro.execution]) that (lexically) contains the point where they were created. This is true even if that evaluation ends in throwing an exception. The value computations and side effects of destroying a temporary object are associated only with the full-expression, not with any specific subexpression.

该临时对象将在给定表达式的最后一步被销毁。

事实上,C++ 依赖于它,因为这个非常常见的表达式可以正常工作:

int x = 0, y = 0, z = 0, t = 0;
int a = x + y + z + t;

因为x+y是一个临时的,x+y+z是另一个临时的。


根据class.temporary#5

中的规则,临时文件的生命周期将缩短

There are three contexts in which temporaries are destroyed at a different point than the end of the full-expression. The first context is when a default constructor is called to initialize an element of an array with no corresponding initializer ([dcl.init]). The second context is when a copy constructor is called to copy an element of an array while the entire array is copied ([expr.prim.lambda], [class.copy]). In either case, if the constructor has one or more default arguments, the destruction of every temporary created in a default argument is sequenced before the construction of the next array element, if any.

并且会按照class.temporary#6中的规则延长:

The third context is when a reference is bound to a temporary.116 The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:

  • A temporary object bound to a reference parameter in a function call ([expr.call]) persists until the completion of the full-expression containing the call.

  • The lifetime of a temporary bound to the returned value in a function return statement ([stmt.return]) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.

  • A temporary bound to a reference in a new-initializer ([expr.new]) persists until the completion of the full-expression containing the new-initializer.

本例中可以看到第一个上下文:

struct bar {
    bar() { std::cout << __func__ << '\n'; }
    bar(const bar&) { std::cout << __func__ << "__\n"; }
    ~bar() { std::cout << __func__ << '\n'; }
};

struct foo {
    foo(const bar& b = bar()) { std::cout << __func__ << '\n'; }
};

int main() {
    foo f[] = {foo(), foo()};
}

以上程序应该输出:

bar
foo
~bar
bar
foo
~bar

第二个上下文将被添加到C++17,从那时起这个程序:

struct bar {
    bar() { std::cout << __func__ << '\n'; }
    bar(const bar&) { std::cout << __func__ << "__\n"; }
    ~bar() { std::cout << __func__ << '\n'; }
};

struct foo {
    foo() {}
    foo(const foo&, const bar& b = bar()) { std::cout << __func__ << "__\n"; }
};

struct foox {
    foo f[2];
};

int main() {
    foox fx;
    foox yx = fx;
}

必须输出:

bar
foo__
~bar
bar
foo__
~bar

第三个上下文,你可以在, and here

中找到答案