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

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

我理解

std::error_code
std::error_condition
之间的区别,并且我理解
std::errc
是“错误条件”枚举,而不是“错误代码”枚举。我了解如何将结果
std::error_code
值与“错误条件”进行比较等等。多年来,我已多次阅读 Chris Kohlhoff 关于这些类型的博客文章。我已在生产代码中使用了这些类型及其扩展。

但是,我不明白为什么

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>
false
std::errc
。据推测,这是为了防止通过
std::errc
隐式地将
std::error_code
转换为
std::make_error_code(std::errc)
,但我不清楚为什么在存在
std::make_error_code(std::errc)
时阻止这种情况是可取的。换句话说,如果你不应该用
std::error_code
来制作
std::errc
,为什么有一个函数可以做到这一点呢?如果你应该这样做,为什么隐式构造函数被禁用?

这是

std::errc
的特殊情况吗,因为代码通常希望在
std::error_code
中实际生成具有
std::generic_category()
值的真实
std::errc
吗?换句话说,
std::errc
在某些方面both是“错误代码”和“错误条件”枚举吗? (如果是这样,为什么不是
std::is_error_code_enum_v<std::errc>
true
?)

是否有其他原因造成的特殊情况?

用户定义的错误条件枚举是否也应该像

make_error_code()
那样提供
std::errc
,或者只是
make_error_condition()

c++ error-code
2个回答
0
投票

std::errc
定义了可移植误差的值;一组简单的(事实上,非常有限的)错误代码值。

std::error_code
是与平台相关的错误,其中包含错误代码值和对错误类别对象的对象的引用。


从程序设计的角度来看,从

std::errc
std::error_code
的隐式转换没有充分的理由。它们在逻辑上是非常不同的错误集: 简单与分组(来源) 标准错误集与环境(平台和标准库实现)相关的错误集。

代码之间的转换不仅仅是改变类型或数字表示,它通常是依赖于环境的逻辑。

用于此类转换的非成员函数是一个非常自然的解决方案。


从实施方面来看, 它们不是基本类型,因此编译器将专门支持类型之间的转换;

std::errc
被实现为
class enum
枚举,并且不能像类那样实现转换运算符函数;
std::error_code
是一个类,因此可以实现
std::errc
中的非显式构造函数,它支持隐式转换,就像
std::io_errc
std::future_errc
(*) 一样,但逻辑上对应于不同的错误集只是不关心
std::error_code
,它会违反面向对象编程的原则(具体来说,这将是对代码实体的不适当的责任分配)。


(*) -

std::io_errc
std::future_errc
(以及其他具有 的
error code enumerations
)可以隐式转换为
std::error_code
,因为它们与
std::error_code
存储相同的错误 -
std::error_code
存储完全相同的错误代码值和错误对象对应类别的引用;错误集只是整个集合的子集,其存储支持
std::error code


隐式转换只会使程序设计变得更糟并且容易出错(具体来说,代码上的责任分配不合逻辑,并且容易发生隐蔽和不需要的转换)。


0
投票

std::errc
具有以下能力:

  1. 它可以用来创建
    error_condition
    ,因为它定义了
    make_error_condition()
  2. 它可以用来创建
    error_code
    ,因为它定义了
    make_error_code()
  3. 它可以参与预期
    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) {
        // …
    }
© www.soinside.com 2019 - 2024. All rights reserved.