c++ 为什么std::make_error_code(std::errc)存在?

xcitsw88  于 2023-08-09  发布在  其他
关注(0)|答案(2)|浏览(122)

我理解std::error_codestd::error_condition之间的区别,我理解std::errc是一个“错误条件”枚举,而不是一个“错误代码”枚举。我理解如何将结果std::error_code值与“错误条件”等进行比较。这些年来我读了Chris Kohlhoff's blog post about these types好几遍。我已经在生产代码中使用了这些类型及其扩展。
但是,我不明白为什么std::make_error_code(std::errc)存在。
似乎没有必要将实际的std::error_code值与std::error_condition值进行 * 比较 *:有std::make_error_condition(std::errc)和转换到std::error_condition和特殊的比较重载。
这个函数的存在特别令人困惑,因为std::is_error_code_enum_v<std::err>falsestd::errc。据推测,这是为了 * 防止 * 通过std::make_error_code(std::errc)隐式地将std::errc转换为std::error_code,但我不清楚 * 为什么 * 在std::make_error_code(std::errc)存在时防止这种情况是可取的。换句话说,如果你不应该用std::errc生成std::error_code,为什么会有这样一个函数呢?如果你应该这样做,为什么要禁用隐式构造函数?
std::errc是否是一个特例,因为代码通常希望在std::generic_category()中实际生成一个值为std::errc的真实的std::error_code?换句话说,std::errc在某种程度上是不是同时是“错误代码”和“错误条件”枚举?(如果是这样,为什么不是std::is_error_code_enum_v<std::errc>true?)
这是一个特殊的情况下,因为一些其他的原因吗?
用户定义的错误条件枚举是否也应该像std::errc一样提供make_error_code(),或者只是make_error_condition()

js81xvg6

js81xvg61#

**std::errc**定义可移植错误的值;一组简单的(实际上非常有限的)错误代码值。
**std::error_code**是一个平台相关错误,包含错误代码值和对错误类别对象的引用。

从程序设计的Angular 来看,从std::errcstd::error_code的隐式转换没有什么好的理由。他们逻辑上非常不同的一套错误:普通与分类(来源)标准错误集与环境(平台和标准库实现)相关的错误集。
代码之间的转换不仅仅是改变类型或数字表示,它通常是环境相关的逻辑。
使用非成员函数进行这种转换是一种非常自然的解决方案。
从实现的Angular 来看,它们不是基本类型,因此编译器将专门支持从类型和到类型的转换; std::errc被实现为class enum枚举,不能像类那样实现转换运算符函数; std::error_code是一个类,因此可以实现std::errc的非显式构造函数,它支持隐式转换,就像std::io_errcstd::future_errc一样(),但是不同错误集合的逻辑对应性并不是std::error_code所关心的,这将违反面向对象编程的原则。(具体地说,这将是代码实体上不适当的责任分配)。
)-std::io_errcstd::future_errc(以及其他error code enumerations)可以隐式地转换为std::error_code,因为它们与std::error_code存储的错误相同-std::error_code存储完全相同的错误代码值和对错误对象的对应类别的引用;错误集只是std::error code支持的整个存储集的子集。
隐式转换只会使程序设计变得更糟,更容易出错(特别是,代码上的责任分配不合逻辑,容易发生隐蔽和不希望的转换)。

4xrmg8kj

4xrmg8kj2#

std::errc具有以下功能:
1.它可以用来创建error_condition,因为它定义了make_error_condition()
1.它可以用来创建error_code,因为它定义了make_error_code()
1.它可以参与需要error_condition的操作,因为它是is_error_condition_enum
如果std::errc可以隐式转换为error_code,它将使用现有的显式转换(make_error_code)来实现可以想象的隐式转换。另一方面,make_error_code本身是有帮助的。
我们可以在一个函数中使用make_error_code,该函数在出错时报告error_code

#include <algorithm>
#include <iostream>
#include <system_error>
#include <utility>
#include <vector>

struct minOrErrorResult {
    int result;
    std::error_code ec;
};

minOrErrorResult minOrError(const std::vector<int>& vec) noexcept {
    if (vec.empty())
        return { {}, make_error_code(std::errc::invalid_argument) };

    int result = *std::min_element(vec.begin(), vec.end());
    return { result, {} };
}

int main() {
    const std::vector<int> examples[] = {
        { 3, 1, 2 },
        {},
    };
    for (auto&& vec : examples) {
        auto [result, ec] = minOrError(vec);
        if (ec)
            std::cout << "error: " << ec.message() << " [" << ec << "]\n";
        else
            std::cout << "result: " << result << "\n";
    }
    return 0;
}

字符串
下面是一个类似的函数示例,它在出错时抛出system_error异常:

int minOrException(const std::vector<int>& vec) {
    if (vec.empty())
        throw std::system_error(make_error_code(std::errc::invalid_argument));

    int result = *std::min_element(vec.begin(), vec.end());
    return result;
}

int main() {
    const std::vector<int> examples[] = {
        { 3, 1, 2 },
        {},
    };
    for (auto&& vec : examples) {
        try {
            auto result = minOrException(vec);
            std::cout << "result: " << result << "\n";
        } catch (std::system_error& ex) {
            std::cout << "error: " << ex.what() << " [" << ex.code() << "]\n";
        }
    }
    return 0;
}


如果不使用make_error_code,我们可以假设std::errc常量不被解释为error_code

// Normally assume that rhs is an `error_condition`, never an `error_code`.
    //                       ↓
    if (lhs == std::errc::invalid_argument) {
        // …
    }

相关问题