[英]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.