struct B {
B() throw();
B(const B&) = default; // implicit exception specification is noexcept(true)
B(B&&, int = (throw Y(), 0)) noexcept;
~B() noexcept(false);
};
int n = 7;
struct D : public A, public B {
int * p = new int[n];
// D::D() potentially-throwing, as the new operator may throw bad_alloc or bad_array_new_length
// D::D(const D&) non-throwing
// D::D(D&&) potentially-throwing, as the default argument for B's constructor may throw
// D:: D() potentially-throwing
};
考虑从except.spec#11引用的上述代码。我对 D 的所有构造函数的异常规范毫无疑问,除了D::D(D&&)
,它遵守以下规则:
为类 X 隐式声明的构造函数,或在其第一个声明中默认没有 noexcept 说明符的构造函数,当且仅当以下任何构造可能抛出异常时,才具有潜在抛出异常规范:
- 在类 X 的构造函数的隐式定义中通过重载决议选择的构造函数来初始化可能构造的子对象,或
- 这种初始化的子表达式,例如默认参数表达式,或者,
- 对于默认构造函数,默认成员初始值设定项。
显然,D::D(D&&)
具有潜在抛出异常规范的规则既不是第一个项目符号也不是第三个项目符号。对于第一条规则,用于初始化类型基子对象的选定构造函数B
是 B(B&&, int = (throw Y(), 0)) noexcept
,它被声明为具有非抛出异常规范。第三条规则适用于默认构造函数。所以只有第二条规则适用于这种情况。
但是,D::D(D&&)
除了表达式throw Y()
被视为D::D(D&&)
.
定义立即子表达式的规则如下:
表达式 e 的直接子表达式是
- e 的操作数的组成表达式
- e 隐式调用的任何函数调用,
- 如果 e 是 lambda 表达式,则由 copy 捕获的实体的初始化以及 init-captures 的初始化程序的组成表达式,
- 如果 e 是函数调用或隐式调用函数,则调用中使用的每个默认参数的组成表达式,或
- 如果 e 创建了一个聚合对象,则初始化中使用的每个默认成员初始值设定项 ([class.mem]) 的组成表达式。
表达式 e 的子表达式是 e 的直接子表达式或 e 的直接子表达式的子表达式。
理解子表达式规则的简单方法是它递归地工作,也就是说,
immediate subexpression of immediate subexpression... of immediate subexpression of e
是 的子表达式e
。
由于第四个项目符号,我同意该表达式throw Y()
是函数的子表达式。B(B&&, int = (throw Y(), 0)) noexcept
但是,我不知道是否B(B&&, int = (throw Y(), 0)) noexcept
被视为由表达式调用的隐式调用函数D::D(D&&)
,这似乎服从第二个项目符号。如果是这样,请考虑以下代码:
class Test{
Test(){}
~Test(){}
};
void func(){
Test t{} // implicitly invoke the defautl constructor of `Test`
// would implicitly invoke the destructor of `Test`
}
int main(){
func();
}
所以,正如我在评论中所写,构造函数和析构函数Test
是否被视为表达式的子表达式func()
?如果不是,如何解释措辞a subexpression of such an initialization
?所以,我的问题是:
Q1:
在第二个示例中,隐式调用的构造函数或析构函数是否被视为表达式的子表达式func()
?
Q2:
如果第一个问题的答案是否定的,那么如何解释a subexpression of such an initialization
?