【发布时间】:2022-11-02 17:50:43
【问题描述】:
我发现运行这个用 clang 编译的小 sn-p 代码有一个奇怪的行为:
#include <iostream>
#include <exception>
#include <typeinfo>
struct Foo : public std::exception {
std::string myString;
Foo(const std::string& str) : myString(str) {}
Foo() : Foo(typeid(*this).name()) {}
};
int main()
{
Foo f;
std::cout << f.myString;
}
在委托构造函数中调用的指令typeid(*this).name() 返回一个导致分段错误的nullptr。在委托构造函数调用期间,std::exception 基类尚未初始化,这似乎是导致此行为的原因。
我想知道这段代码是否由于某种原因格式错误,或者这种行为是预期的。
我无法使用 g++ 重现此错误,其中代码运行良好。
它也仅在基类是 std::exception 时才会发生,在任何其他情况下,即使在 clang 上它也能正常工作。
【问题讨论】:
-
这个错误有点道理。此时基类尚未初始化,因为尚未执行基构造函数。
-
@HolyBlackCat 奇怪的部分是它仅在基类为 std::exception 并且代码使用 clang++ 编译时发生,否则我无法重现它。
-
我倾向于同意@HolyBlackCat。通常的 C++ 规则仍然适用。
*this是一个指针解引用,它有一些常见的警告。如果该地址(还)没有对象,则只能以有限的方式使用生成的引用。typeid不是其中之一:它查询对象的动态类型,因此必须有一个对象。所以这个问题可能有点绕,但逻辑似乎很简单。未定义的行为,任何事情都可能发生,实际结果可能纯属巧合。 -
任何具有虚拟成员的基类都会失败:godbolt.org/z/Kh4G3fYG3,MSVC 显示相同的行为。尽管即使使用非虚拟基础,它仍然是未定义的行为,它只是碰巧起作用
-
如果我错了,请纠正我,但这意味着问题在于默认构造函数委托构造,对吗?因此,如果您将其更改为:
Foo() : myString(typeid(*this).name()) {},那么基础将首先(默认)构建,并且定义明确。
标签: c++ exception inheritance language-lawyer clang++