考虑使用LLVM system_category
实现编写的自定义错误类型,以供参考:
#include <iostream>
#include <system_error>
struct my_error_category_type : std::error_category {
char const* name() const noexcept override { return "name"; }
std::string message(int i) const noexcept override{ return "message"; }
~my_error_category_type() {
std::cout << "Destroyed the category" << std::endl;
}
};
std::error_category const& my_error_category() noexcept {
static my_error_category_type c;
return c;
}
现在想象一下下面的简单类,它使用std::error_code
处理错误:
std::error_code do_some_setup() {
return std::error_code(1, my_error_category());
}
std::error_code do_some_cleanup() {
return std::error_code(2, my_error_category());
}
struct MyObj {
void method() {
// this constructs the category for the first time
auto err = do_some_setup();
std::cout << err << std::endl;
}
~MyObj() {
std::cout << "Running cleanup" << std::endl;
auto err = do_some_cleanup();
std::cout << err << std::endl;
}
};
以下代码提供警报输出
static MyObj obj;
int main() {
obj.method(); // remove this line, and the output is fine
}
name:1
Destroyed the category
Running cleanup
name:2
请注意如何在已破坏的对象上调用my_error_category_type::message
!
我的问题是:
message
是否安全?std::system_category()
对象等的寿命提供任何保证?我上面链接到的LLVM实现遇到了完全相同的问题。答案 0 :(得分:3)
调用对象方法的析构函数后,调用它是不安全的。
问题是如何控制对象的破坏顺序。静态是以相反的初始化顺序破坏的,因此my_error_category_type将在MyObj之前被破坏,因为其构造函数是在MyObj构造函数之后调用的。这不是问题,需要通过标准解决,而是架构问题。
因此,我们必须以某种方式控制销毁顺序。
最简单的方法是确保提前调用obj
析构函数:
void F() {
MyObj obj;
obj.method();
}
int main() {
F();
}
程序输出:
name:1
Running cleanup
name:2
Destroyed the category
现在MyObj
的析构函数被更早地调用,而不是在main之后,而是在F()
结束后被调用,因为MyObj
是作用域变量,并且在F()
完成并{ {1}}在main完成时被破坏。
但是,如果我们仍然想使MyObj成为静态对象,则有一种称为 Nifty Counter Idiom 的技术,该技术仅在上次使用后才有助于消除静电。但它有其权衡。 link
与静态相似的问题:“静态初始化顺序惨败”(link)。
答案 1 :(得分:-1)
这似乎是std::system_category
实施中的错误。
作为一种变通方法,MyObj
构造函数可以调用std::system_category
/ my_error_category
,以便在构造MyObj
之前构造函数静态错误类别,因此只能将其销毁在MyObj
被销毁之后:
MyObj::MyObj() noexcept { static_cast<void>(my_error_category()); }