7.3.1 Lvalue-to-rvalue conversion

1

一个非函数、非数组类型T的泛左值可以被转换为纯右值:

  • 如果T是不完全类型,则需要执行此转换的程序是非法的(ill-formed)。

  • 如果T是非类类型,则所得纯右值的类型为去掉cv限定的T。

  • 否则,所得纯右值的类型就是T本身。

2

当对表达式E执行左值到右值转换时,如果:

  • (2.1) E不是潜在可求值,或者
  • (2.2) 对E的求值会进一步导致对其潜在结果中的某个成员Eₓ求值,而该Eₓ所命名的变量x并未被odr-used([basic.def.odr])。

则不会访问引用对象中所包含的值。【示例:

1
2
3
4
5
6
7
8
9
10
struct S { int n; };
auto f()
{
S x { 1 };
constexpr S y { 2 };
return [&](bool b) { return (b ? y : x).n; };
}
auto g = f();
int m = g(false); // undefined behavior: access of x.n outside its lifetime
int n = g(true); // OK, does not access y.n
3

泛左值的转换的结果由下列规则决定:

  • (3.1) 若T是cv std::nullptr_t,结果为一个空指针常量。【注释:因为转换不会访问泛左值所引用的对象,所以即使T带有volatile限定([intro.wxecution])也不会产生副作用;并且该泛左值可以引用联合([class.union])中当前非活跃的成员。——注释结束】

  • (3.2) 否则,若T为类类型,则此转换会以该泛左值为源,对结果对象执行拷贝初始化。

  • (3.3) 否则,若泛左值所引用的对象中包含无效的指针值([basic.stc.dynamic.deallocation][basic.stc.dynamic.safety]),则行为由实现定义。

  • (3.4) 否则,读取([defns.access])泛左值所指示的对象,该对象中包含的值是纯右值。

Paw5zx个人理解:点击查看更多
  • (3.1) 类型为std::nullptr_t的泛左值,到右值的转换结果就是空指针常量,转换过程不会访问任何对象也没有副作用
1
2
std::nullptr_t np = nullptr;    // np是左值
auto p = np; // 对np做lvalue-to-rvalue后得到纯右值std::nullptr_t,用来拷贝初始化p
  • (3.2) 类类型的泛左值,到右值的转换会调用拷贝/移动构造函数生成一个新的临时对象而不是直接按字节拷贝
1
2
3
4
5
6
7
8
9
10
11
struct S
{
S() = default;
S(const S&) { std::cout << "copy\n"; }
};

int main()
{
S s1; //s1是左值
S s2 = static_cast<S>(s1); // static_cast<S>(s1)发生lvalue-to-rvalue conversion,编译器拷贝构造一个临时对象,这个临时对象是纯右值
}
  • (3.3) 包含无效指针,行为由实现定义
  • (3.4) 直接读内存值。
1
2
double a = 3.14;
double b = a + 2.0; // a发生lvalue-to-rvalue转换
4

【注释:也可参见[basic.lval]。——注释结束】

7.3.4 Temporary materialization conversion

4

类型为T的纯右值可以转换为类型为T的将亡值。此转换通过将临时对象作为结果对象来求值该纯右值(即计算这个纯右值的表达式),从而用该纯右值初始化一个类型为T的临时对象([class.temporary]),并产生一个表示该临时对象的将亡值。类型T必须是完全类型。【注释:若T是类类型(或其数组),它必须具有可访问且未删除的析构函数;见[class.dtor]。——注释结束】

【示例:

1
2
struct X { int n; };
int k = X().n; // OK, X() prvalue is converted to xvalue

——示例结束】

Paw5zx个人理解:点击查看更多

临时物化是让原来的纯右值求值结果直接构造到一个对象上(返回对象)(而不是先求值,在拷贝到该对象上),这个对象在语义上是将亡值。

还是鸡巴得学临时对象

这就是之前的URVO?

1
2
struct X { int n; };
int k = X().n; // OK, X() prvalue is converted to xvalue
  • X()是纯右值

  • X().n需要访问成员n,要求有个对象

  • 触发临时物化,X()

  • ①创建一个类型为T的临时对象

  • ②用纯右值来初始化这个临时对象

  • ③产生一个将亡值,它表示这个刚创建的临时对象