gpt4 book ai didi

C++11 返回值和错误对

转载 作者:行者123 更新时间:2023-11-30 01:38:25 26 4
gpt4 key购买 nike

由于 C++11 引入了移动语义,我想知道该函数是否可以同时返回值和操作状态。实际上,实现起来并不难,但我即将开始一个新的庞大项目,不知道我应该走这条路,还是使用一些老式的东西。所以我很好奇你的看法。

请考虑以下符号:

class File
{
FILE *file = nullptr;

public:

Result<void> open(const char *fileName);
Result<void> close();
Result<size_t> size();
Result<void> seek(size_t newPosition);
Result<size_t> position();
Result<char> readCharacter();
};

现在让我们分析一个用法示例:

Result<void> processFile(const char *fileName)
{
File file;
auto result = file.open(fileName);
if (!result.isSuccess())
return result;

auto fileSize = file.size();
if (!fileSize.isSuccess())
return fileSize;

for (size_t i = 0; i < fileSize; i++) {
auto character = file.readCharacter();
if (!character.isSuccess())
return character;

if (character < 'a' || character > 'z')
return Error::invalidData;

// processfileCharacter(character);
}

return Error::success;
}

如您所见,错误管理变得极其简单。此外,当我编写仅包含 header 的代码时,启用优化后 GCC 和 MSVC 都会生成非常优化的代码。我非常喜欢这种表示法,没有看到任何严重的缺点。但我很想听听您的意见。

实现

如需测试,请欣赏代码:

enum class Error: int
{
success,
unknown,
invalidData
// ...
};

结果类:

template <typename Type = void>
class Result
{
Error error = Error::success;
Type data;

public:

Result() = default;

Result(Result &&result) = default;
Result(const Result &result) = default;
template <typename OtherType> Result(const Result<OtherType> &result) : error(result.error) {}

Result & operator =(Result &&result) = default;
Result & operator =(const Result &result) = default;
template <typename OtherType> Result & operator =(const Result<OtherType> &result) { error = result; return *this; }

Result(const Type &data) : data(data) {}
Result(Type &&data) : data(std::move(data)) {}
Result(const Error &error) : error(error) {}
Result(Error &&error) : error(std::move(error)) {}

operator Type& () { return data; }
operator const Type& () const { return data; }
operator const Error() const { return error; }

bool isSuccess() const { return error == Error::success; }
};

void 特化:

template <>
class Result<void>
{
Error error = Error::success;

public:

Result() = default;

Result(Result &&result) = default;
Result(const Result &result) = default;
template <typename OtherType> Result(const Result<OtherType> &result) : error(result.error) {}

Result & operator =(Result &&result) = default;
Result & operator =(const Result &result) = default;
template <typename OtherType> Result & operator =(const Result<OtherType> &result) { error = result; return *this; }

Result(const Error &error) : error(error) {}
Result(Error &&error) : error(std::move(error)) {}

operator const Error() const { return error; }

bool isSuccess() const { return error == Error::success; }
};

最佳答案

这种方法有以下主要缺点:

  • 当您忘记检查结果时,它会打开您的代码库以应对静默故障的可能性。

    请考虑将此代码作为客户端代码示例:

    Result<void> processFile(const char *fileName)
    {
    File file;
    auto result = file.open(fileName);
    // utnapistim was tired when writing this code and forgot to
    // check the error status in result
    // (this is a bug)

    auto fileSize = file.size(); // (1)
    if (!fileSize.isSuccess())
    return fileSize;

    // ...
    // rest is the same as your example client code

    return Error::success;
    }

    缺少错误检查下方的代码无提示地失败:它将在不应该执行的情况下执行,并且数据无效。

    在这种特殊情况下,执行的代码(第 (1) 行)位于 File 类中,这可以工作正确(如果 File 类在获取大小之前检查内部状态)。

    使用您的方法,无论何时编写客户端代码,您都必须明确记住要处理错误。在大多数实际情况下,您会假设 File::size 在调用低级文件大小函数之前检查状态

    不要假设 - 它会导致错误。

  • 它会严重膨胀所有客户端代码,努力完成编译器的工作。考虑这个替代的客户端代码:

    void processFile(const char *fileName)
    {
    auto file = File{fileName}; // throws on failure
    auto fileSize = file.size(); // executed only on success

    for (size_t i = 0; i < fileSize; i++) {
    auto character = file.readCharacter(); // throws on failure

    if (character < 'a' || character > 'z')
    throw invalidData{'expected alphanumeric value'};

    // processfileCharacter(character);
    }

    return Error::success;
    }

    您需要维护的客户端代码更少,而且代码看起来更简单

    你有不变量(当你在 File 实例的声明之下时,你知道它是有效的,而不必添加 if声明)

  • 它严重限制了 OO 设计的良好原则:

    • 当你的构造函数无法运行时会发生什么?您的 File 类没有。

如果您改为使用异常,则这不是问题:无论您是否对它们进行测试(catch block ),异常都会传播。

除非您有充分的理由避免异常,您应该使用它们进行错误处理

关于C++11 返回值和错误对,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47903169/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com