我希望我的函数返回成功的指示或描述失败性质的对象。我通常会为此使用异常,但我被告知不要将它们用于通用代码路径,并且由于各种原因,这组函数可能会经常失败。如何从函数中返回成功或错误对象?
我的想法是使用C++ 17的std::optional
,因为我不必在不需要时返回完整的错误对象。因此,对于可选的,如果函数不成功,则返回错误对象,否则可选项为空。与此相关的问题是它逆转了对返回值的期望(即true
通常表示成功而非失败)。
我可以让人们使用is_success
功能,这将是这样被使用,假设Error
是我的错误类:
auto result = do_stuff();
if (!is_success(result)) {
Error err = *result;
// ...
}
还是会因此类更为强劲?
class MaybeError {
std::optional<Error> _error;
public:
MaybeError(const Error& error) : _error(error) {}
constexpr MaybeError() : _error({}) {}
explicit operator bool const() {
return !(_error.operator bool());
}
constexpr bool has_error() const {
return !(_error.has_value());
}
constexpr Error& error() & { return _error.value(); }
constexpr const Error & error() const & { return _error.value(); }
constexpr Error&& error() && { return std::move(_error.value()); }
constexpr const Error&& error() const && { return std::move(_error.value()); }
constexpr const Error* operator->() const { return _error.operator->(); }
constexpr Error* operator->() { return _error.operator->(); }
constexpr const Error& operator*() const& { return _error.operator*(); }
constexpr Error& operator*() & { return _error.operator*(); }
constexpr const Error&& operator*() const&& { return std::move(_error.operator*()); }
constexpr Error&& operator*() && { return std::move(_error.operator*()); }
};
这同样可以使用在第一个例子:
auto result = do_stuff();
if (!result) {
Error err = *result;
// ...
}
什么是最好的选择吗?我是否以正确的方式去做这件事?
编辑要明确的是,do_stuff
功能被呼叫如果成功,不返回的对象。如果它总是成功没有错误,它只是void do_stuff()
。
编辑2在评论克里斯蒂安·哈克建议使用一个简单的结构较不超工程解决方案。
struct MaybeError {
std::optional<Error> error;
};
这将是既简单,解决我的担心,人们会期望通过功能摆明,这是所要检测的错误情况返回如果成功的话属实。例如:
auto result = do_stuff();
if (result.error) {
Error e = *t.error;
// ...
}
这看起来非常复杂,当使用异常时会更清晰和更简单,并且涉及更少的代码。 –
我很乐意使用例外,但不幸的是,我的老板对这个问题有着强烈的感受。 – ChrisD
他是个白痴。良好C++代码中的异常不是可选的。 –