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

时间:2016-11-22 06:42:29

标签: c++ bitset

我一直在查看bitset标准C ++库头的头文件。我发现重载的operator [] operator[](size_t ndx)(在类bitset中定义)返回类reference的temproray对象。

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

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

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

reference类定义了重载的=运算符成员函数,以便上面的示例可以工作:

 //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]首先返回类reference的临时对象,然后在返回的临时对象上调用重载的运算符(operator bool() const),将其转换为bool数据类型

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

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

C和C ++中临时对象的生命周期是什么?

2 个答案:

答案 0 :(得分:2)

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

  

当实现引入具有非平凡构造函数([class.ctor],[class.copy])的类的临时对象时,它应确保为临时对象调用构造函数。类似地,析构函数应该用一个非平凡的析构函数([class.dtor])来调用。 临时对象在评估全表达式([intro.execution])的最后一步时被销毁,该表达式(词法)包含创建它们的点。即使该评估以投掷结束也是如此一个例外。销毁临时对象的值计算和副作用仅与完整表达式相关联,而不与任何特定子表达式相关联。

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

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

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

由于x+y是临时的,x+y+z是另一个临时的。

根据class.temporary#5

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

有三种情境可以在与完整表达结束时不同的时刻销毁临时对象。第一个上下文是调用默认构造函数来初始化没有相应初始值设定项的数组元素([dcl.init])。第二个上下文是在复制整个数组时调用复制构造函数来复制数组元素([expr.prim.lambda],[class.copy])。在任何一种情况下,如果构造函数具有一个或多个默认参数,则在构造下一个数组元素(如果有)之前,对默认参数中创建的每个临时文件的销毁进行排序。

并且会根据class.temporary#6中的规则延长:

  

第三个上下文是将引用绑定到临时值.11引用绑定的临时值或作为绑定引用的子对象的完整对象的临时值在引用的生命周期内持续存在除外:

     
      
  • 在函数调用([expr.call])中绑定到引用参数的临时对象会一直存在,直到包含该调用的完整表达式完成为止。

  •   
  • 函数返回语句([stmt.return])中返回值临时绑定的生命周期未扩展;临时在return语句中的full-expression结束时被销毁。

  •   
  • 在new-initializer([expr.new])中对引用的临时绑定一直持续到包含new-initializer的full-expression完成为止。

  •   

在这个例子中可以看到第一个上下文:

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

对于第三个上下文,您可以在herehere

中找到答案

答案 1 :(得分:1)

包含if (b[2])的{​​{1}}中的条件是单个表达式,临时值对表达式的整个生命周期都有效。