"Most important const" 有条件表达式?
"Most important const" with conditional expression?
考虑以下代码:
int foo(MyClass const* aPtr = 0) {
MyClass const& a = aPtr ? *aPtr : MyClass(); // Either bind to *aPtr, or to a default-constructed MyClass
...
return a.bar();
}
"most important const" 希望在这里使用。目的是允许传入一个 null aPtr
(顺便说一句,是的,它必须是一个指针参数),在这种情况下,一个临时的 MyClass
对象将被默认构造,并且它的生命周期通过绑定到它的 const 引用扩展。然而,如果 aPtr
不为空,则引用将绑定到其指向的对象,而不会发生任何(昂贵的)复制构造。
两个问题是:
- 如果
aPtr == 0
,a
是否保证在函数结束之前引用有效的 MyClass
对象?
- 如果
aPtr != 0
,a
会绑定到它,而不是其他一些 MyClass
吗?
根据测试,1 的答案几乎可以肯定是 "yes"。 #2 我不太确定,虽然(复制省略等)......条件表达式似乎最终可能会从 *aPtr
复制构造一个临时 MyClass
,并扩展那的生活是暂时的。
首先,是的 a
保证引用有效的 MyClass
对象。这直接来自 [class.temporary]/4-5:
There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression. The first context is when a default constructor is called [...]
The second context is when a reference is bound to a temporary. 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 bound to a reference member in a constructor’s ctor-initializer [...]
- A temporary bound to a reference parameter in a function call [...]
- The lifetime of a temporary bound to the returned value in a function return statement [...]
- A temporary bound to a reference in a new-initializer [...]
None 这些例外情况适用。
如果 aPtr
是一个有效的指针,那么会创建一个副本,因为 aPtr ? *aPtr : MyClass{}
的类型只是 MyClass
。该临时文件绑定到 a
,并且 它的 生命周期也出于同样的原因而持续存在。
您的条件表达式是纯右值(因为它的操作数之一是)。如果选择了条件运算符的第一个替代项,则将其转换为临时项(产生副本)。该临时绑定到引用,并且适用通常的生命周期扩展。
相关标准语[expr.cond]:
If the operands have class type, the result is a prvalue temporary of the result type, which is copy-initialized from either the second operand or the third operand depending on the value of the first operand.
关于 1) 请参阅上面 kerek 的回答
关于2)标准中关于条件运算符的说法:
5.16/4: If the second and third operands are glvalues of the same value category and have the same type, the result is of that type and value category (...).
5.16/5: Otherwise, the result is a prvalue. (...)
根据 3.10/1 中左值和右值的分类,*aPtr
是左值,MyClass()
是纯右值。因此,结果应该是一个纯右值,这样引用就应该引用这个临时文件(可能是一个复制构造的临时文件)。
编辑: 这里有一个online demo,这表明const引用是指临时的而不是原始的aPtr 指向的对象。
考虑以下代码:
int foo(MyClass const* aPtr = 0) {
MyClass const& a = aPtr ? *aPtr : MyClass(); // Either bind to *aPtr, or to a default-constructed MyClass
...
return a.bar();
}
"most important const" 希望在这里使用。目的是允许传入一个 null aPtr
(顺便说一句,是的,它必须是一个指针参数),在这种情况下,一个临时的 MyClass
对象将被默认构造,并且它的生命周期通过绑定到它的 const 引用扩展。然而,如果 aPtr
不为空,则引用将绑定到其指向的对象,而不会发生任何(昂贵的)复制构造。
两个问题是:
- 如果
aPtr == 0
,a
是否保证在函数结束之前引用有效的MyClass
对象? - 如果
aPtr != 0
,a
会绑定到它,而不是其他一些MyClass
吗?
根据测试,1 的答案几乎可以肯定是 "yes"。 #2 我不太确定,虽然(复制省略等)......条件表达式似乎最终可能会从 *aPtr
复制构造一个临时 MyClass
,并扩展那的生活是暂时的。
首先,是的 a
保证引用有效的 MyClass
对象。这直接来自 [class.temporary]/4-5:
There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression. The first context is when a default constructor is called [...]
The second context is when a reference is bound to a temporary. 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 bound to a reference member in a constructor’s ctor-initializer [...]
- A temporary bound to a reference parameter in a function call [...]
- The lifetime of a temporary bound to the returned value in a function return statement [...]
- A temporary bound to a reference in a new-initializer [...]
None 这些例外情况适用。
如果 aPtr
是一个有效的指针,那么会创建一个副本,因为 aPtr ? *aPtr : MyClass{}
的类型只是 MyClass
。该临时文件绑定到 a
,并且 它的 生命周期也出于同样的原因而持续存在。
您的条件表达式是纯右值(因为它的操作数之一是)。如果选择了条件运算符的第一个替代项,则将其转换为临时项(产生副本)。该临时绑定到引用,并且适用通常的生命周期扩展。
相关标准语[expr.cond]:
If the operands have class type, the result is a prvalue temporary of the result type, which is copy-initialized from either the second operand or the third operand depending on the value of the first operand.
关于 1) 请参阅上面 kerek 的回答
关于2)标准中关于条件运算符的说法:
5.16/4: If the second and third operands are glvalues of the same value category and have the same type, the result is of that type and value category (...).
5.16/5: Otherwise, the result is a prvalue. (...)
根据 3.10/1 中左值和右值的分类,*aPtr
是左值,MyClass()
是纯右值。因此,结果应该是一个纯右值,这样引用就应该引用这个临时文件(可能是一个复制构造的临时文件)。
编辑: 这里有一个online demo,这表明const引用是指临时的而不是原始的aPtr 指向的对象。