1

考虑使用LLVMsystem_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在被破坏的对象上调用!

我的问题是:

  1. 调用message这个被破坏的对象安全吗?
  2. 如果没有,有没有办法保持类别的生命周期?我可以以某种方式使对象不朽吗?
  3. 该标准是否对内置std::system_category()对象的生命周期等做出任何保证?我在上面链接到的 LLVM 实现遇到了完全相同的问题。
4

3 回答 3

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()end之后,因为MyObj它是一个作用域变量,它在F()finish之后static my_error_category_type c被破坏,在main完成时被破坏。

但是,如果我们仍然想让 MyObj 成为静态,有一种称为Nifty Counter Idiom的技术,它有助于在最后一次使用后才破坏静态。但它有其权衡。关联

与静态类似的问题:“静态初始化顺序惨败”(链接)。

于 2019-03-01T08:42:20.490 回答
0

静态std::error_category析构函数中不能安全地使用静态。

这正是“静态初始化命令失败”(ISO C++ 超级常见问题解答),但在销毁时,所以 - “静态破坏命令失败”:你不能保证任何事情,或者至少你很难知道是否和关于静态持续时间对象被破坏的顺序,您可以得到什么保证。

因此,您不能依赖此顺序,特别是静态存储持续时间对象(全局变量或局部范围静态变量)的析构函数不能假定任何其他静态对象,std::error_category例如-破坏。

@DanielZ 建议通过将示例中的变量之一设置为范围来规避问题 - 这显然会起作用。我建议不要尝试操纵或设计破坏顺序,即使这是可能的,因为这会导致代码脆弱和混乱。

于 2022-01-14T21:41:54.093 回答
-1

它看起来像std::system_category.

作为一种变通方法,MyObj构造函数可以调用std::system_category/ my_error_category,以便在构造函数之前MyObj构造函数静态错误类别,因此仅在销毁之后才MyObj销毁:

MyObj::MyObj() noexcept { static_cast<void>(my_error_category()); }
于 2019-03-01T10:40:26.647 回答