首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++错误处理-使用std::pair或std::tuple返回错误代码和函数的缺点

问C++错误处理-使用std::pair或std::tuple返回错误代码和函数的缺点
EN

Stack Overflow用户
提问于 2012-12-08 21:40:09
回答 4查看 3K关注 0票数 8

不深入一般异常与错误代码的讨论,您认为使用std::pair或std:tuple返回多个值(即函数的返回值和错误/成功代码,类似于多少个Go developers apparently do error handling )有什么缺点

显然,这种方法的优点是不必使用函数返回值或错误代码的参数(这取决于您喜欢使用哪种方式)。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-12-08 23:27:29

“您认为使用std::pair或std:tuple返回多个值,即函数的返回值和错误/成功代码的缺点是什么?”

这种简单(C级)故障处理方法的主要缺点是

  • loss of safety。

也就是说,还有更多可能出错的地方,比如访问不确定的结果值。或者当函数没有产生有意义的返回值时,只使用返回值。

旧的Barton & Nackman Fallow类通过限制对结果值的访问解决了这个安全问题。本质上,调用代码在使用结果值之前必须检查是否有结果值,并且使用逻辑上不存在的结果值会导致异常或终止。boost::optional类的作用与此大致相同。

如果你不想依赖Boost,那么为POD结果类型实现一个Optional类是很容易的,而且以可能的低效(动态分配)为代价,你可以使用一个std::vector来携带一个非POD可能的结果。

挑战在于保持调用代码的清晰度,这是练习…的全部要点

票数 1
EN

Stack Overflow用户

发布于 2012-12-08 21:56:22

这个“习惯用法”很好,因为类型和成功指示器都是函数的返回值。失败可能不是异常,所以异常有时是不合适的。

然而,缺点是您必须拆分这两种返回类型。这可能很难看;使用std::tie很有帮助,但您无法从多个返回构造。

代码语言:javascript
复制
bool success;
std::string value;
std::tie(success, value)=try_my_func();

这是相当冗长的。

其次,如果其中一个类型是“可选的”,取决于元组中另一个元素的值,那么它仍然需要构造,这对于某些类型来说仍然是非常浪费的。

如果您经常使用这个习惯用法,请考虑使用类似boost::optional类型的类型。这可能比go的多重回报更接近haskel。

参考文献

http://www.boost.org/doc/libs/1_52_0/libs/optional/doc/html/index.html

票数 2
EN

Stack Overflow用户

发布于 2012-12-08 22:14:22

为此,在大多数情况下,我使用自己的包装器类型,它引入了一些语法糖。让我们看一个例子:

代码语言:javascript
复制
template <class T>
struct Result
{
public:
    enum Status {
        Success,
        Error
    };

    // Feel free to change the default behavior... I use implicit
    // constructors for type T for syntactic sugar in return statements.
    Result(T resultValue) : s(Success), v(resultValue) {}
    explicit Result(Status status, std::string errMsg = std::string()) : s(status), v(), errMsg(errMsg) {}
    Result() : s(Error), v() {} // Error without message

    // Explicit error with message
    static Result error(std::string errMsg) { return Result(Error, errMsg); }

    // Implicit conversion to type T
    operator T() const { return v; }
    // Explicit conversion to type T
    T value() const { return v; }

    Status status() const { return s; }
    bool isError() const { return s == Error; }
    bool isSuccessful() const { return s == Success; }
    std::string errorMessage() const { return errMsg; }

private:
    T v;
    Status s;

    // if you want to provide error messages:
    std::string errMsg;
};

然后,简单地在你的方法中使用这个类作为返回值,这可能会返回错误:

代码语言:javascript
复制
Result<int> fac(int n) {
    if(n < 0)
        return Result<int>::error("n has to be greater or equal zero!");
    if(n == 0)
        return 1;
    if(n > 0)
        return n * fac(n-1);  // gets automatically converted to int
}

当然,阶乘函数的这个实现很可怕,但它演示了转换,而没有考虑我们使用的错误扩展的返回类型。

示例用法:

代码语言:javascript
复制
int main() {
    for(int i = -3; i < 4; ++i)
    {
        Result<int> r = fac(i);
        std::cout << i << " | ";
        std::cout << (r.isSuccessful() ? "ok" : "error") << " | ";
        if(r.isSuccessful())
            std::cout << r.value();
        else
            std::cout << r.errorMessage();
        std::cout << std::endl;
    }
}

输出:

代码语言:javascript
复制
-3 | error | n has to be greater or equal zero!
-2 | error | n has to be greater or equal zero!
-1 | error | n has to be greater or equal zero!
0 | ok | 1
1 | ok | 1
2 | ok | 2
3 | ok | 6

自定义类型的一大优点是您可以插入一些控件,确保客户端代码总是在访问实际值之前检查错误,并且仅在成功时才访问该值,否则将显示错误消息。

代码语言:javascript
复制
struct Result
{
public:
    // in all constructors, add:
    Result(...) : ..., checked(false) {...}

    // in the error checker methods, add: (and drop const-ness)
    bool is...() { checked = true; return ... }

    // rewrite the value conversion as follows:
    operator T() const { std::assert(checked && isSuccessful()); return v; }
    T value() const    { std::assert(checked && isSuccessful()); return v; }

    // rewrite the errorMessage-getter as follows:
    std::string errorMessage() const { std::assert(checked && isError()); return errMsg; }

private:
    ...
    bool checked;
};

您可能希望根据构建模式(调试构建/发布构建)进行类定义。

请注意,必须按如下方式重写该示例:

代码语言:javascript
复制
Result<int> fac(int n) {
    if(n < 0)
        return Result<int>::error("n has to be greater or equal zero!");
    if(n == 0)
        return 1;
    if(n > 0) {
        Result<int> r = fac(n - 1);
        if(r.isError()) return r;  // propagate error (similar to exceptions)
        return n * r;              // r gets automatically converted to int
    }
}

上面的主代码仍然有效,因为它在访问值/错误消息之前已经进行了错误检查。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13777987

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档