簡體   English   中英

C ++ 11返回值和錯誤對

[英]C++11 Return value and error pair

當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;
}

如您所見,錯誤管理變得極為簡單。 此外,當我編寫僅標頭的代碼時,啟用優化后,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; }
};

虛空專精:

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在調用低級文件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類沒有一個。

如果改用異常,則這不是問題:無論是否對異常進行了測試(捕獲塊),異常都會傳播。

除非有充分的理由避免異常, 否則應將其用於錯誤處理

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM