C++ 中通过 `const` 值捕获异常。编译器存在分歧

问题描述 投票:0回答:1

在以下程序中,struct

A
具有复制构造函数
A(const A&)
和来自左值引用
A(A&)
的构造函数。然后抛出一个
A
的物体,然后将其捕获为
const A
:

#include <iostream>

struct A {
    A() {}
    A(A&) { std::cout << "A(A&) "; }
    A(const A&) { std::cout << "A(const A&) "; }
};

int main() {
    try {
        throw A{};
    }
    catch ( const A ) {
    }
}

所有编译器都接受该程序。

据我所知,异常对象从来都不是 cv 限定的,并且处理程序变量是从引用它们的左值初始化的。因此,人们可以预期

A(A&)
构造函数在
catch
中是首选。 Clang 确实这样做了。

但是 GCC 更喜欢复制构造函数打印

A(const A&)
。演示:https://gcc.godbolt.org/z/1an5M7rWh

在 Visual Studio 2019 16.11.7 中发生了更奇怪的事情,它在程序执行期间不打印任何内容。

这里是哪个编译器?

c++ exception initialization language-lawyer
1个回答
0
投票

综上所述,clang 是正确的。 GCC 调用错误的构造函数,MSVC 非法执行复制省略。

有两个独立的对象

要理解所需的行为,我们必须了解以下代码中,有两个对象:

int main() {
    try {
        throw A{};
    }
    catch ( const A ) {
    }
}

首先,[ except.throw] p3 声明

抛出异常会初始化一个临时对象,称为异常对象。 [...]

其次 [ except.handle] p14.2 解释,

exception-declaration 声明的类型为 cv

T
cv
T&
的变量是从类型为
E
的异常对象初始化的,如下所示:

  • [...]
  • 否则,该变量将从指定异常对象的
    E
    类型的左值复制初始化。

GCC 调用了错误的构造函数

发生的情况类似于:

A temporary = throw A{};
const A a = temporary;

handler中的变量是

const
这一事实不会影响临时对象的cv资格,因为它们是两个独立的对象。 临时对象不是
const
,因此
A(A&)
在初始化期间更匹配。 GCC 错了。

MSVC 非法执行复制省略

此外,可能会执行复制省略。 该标准甚至在 [ except.throw] p7:

中有一个示例
int main() {
  try {
    throw C();      // calls std​::​terminate if construction of the handler's
                    // exception-declaration object is not elided
  } catch(C) { }
}

但是,查看[class.copy.elision] p1.4

时是不允许的

[...] 复制省略,在以下情况下是允许的 ([...]):

  • [...]
  • 当异常处理程序的Exception-declaration声明一个相同类型的对象(cv-qualification除外)作为异常对象时,可以通过[...]省略复制操作

A
const A
的简历资格不匹配,因此不允许复制省略。

© www.soinside.com 2019 - 2024. All rights reserved.