我可以/应该使用 std::exception's 进行常规错误处理吗?
Can I / Should I use std::exception's for regular error handling?
我将在C++开始这个新项目,并且正在考虑一种不痛苦的错误处理方法。现在,我不会开始抛出和捕获异常,并且很可能根本不会抛出异常,但我在想 - 即使是常规的错误处理,为什么要滚动我自己的/复制粘贴一个类来描述错误/状态,当我可以使用std::exception
及其子类(或者可能是std::optional<std::exception>
)?
using Status = std::optional<std::exception>;
Status somethingThatMayFail(int x);
是否有人/任何项目以这种方式工作?这是一个荒谬的想法还是有点吱吱作响?
我认为你不应该构造异常,除非你真的打算抛出它们。我会推荐布尔或枚举返回类型。对于阅读您的代码的人来说,意图会更清晰,而且速度会更快。但是,如果您构造一个异常,其他人会认为他们可以抛出异常并导致整个系统崩溃。
C++异常在资源管理、触发析构函数和所有这些 (RAII) 中起着重要作用。以任何其他方式使用它们都会损害性能,并且(更重要的是)以后会混淆任何试图维护代码的人。
但是,您可以使用与 std::exception 无关的状态报告类执行所需的操作。人们在不需要的时候为"更快"的代码做了太多的事情。如果状态枚举不够好,并且您需要返回更多信息,则状态报告类将起作用。如果它使代码更易于阅读,那就去做吧。
只是不要称它为例外,除非你真的抛出它。
我认为仅性能可能会有问题。请考虑以下代码:
#include <iostream>
#include <chrono>
#include <ctime>
#include <stdexcept>
#include <boost/optional.hpp>
int return_code_foo(int i)
{
if(i < 10)
return -1;
return 0;
}
std::logic_error return_exception_foo(int i)
{
if(i < 10)
return std::logic_error("error");
return std::logic_error("");
}
boost::optional<std::logic_error> return_optional_foo(int i)
{
if(i < 10)
return boost::optional<std::logic_error>(std::logic_error("error"));
return boost::optional<std::logic_error>();
}
void exception_foo(int i)
{
if(i < 10)
throw std::logic_error("error");
}
int main()
{
std::chrono::time_point<std::chrono::system_clock> start, end;
start = std::chrono::system_clock::now();
for(size_t i = 11; i < 9999999; ++i)
return_code_foo(i);
end = std::chrono::system_clock::now();
std::cout << "code elapsed time: " << (end - start).count() << "sn";
start = std::chrono::system_clock::now();
for(size_t i = 11; i < 9999999; ++i)
return_exception_foo(i);
end = std::chrono::system_clock::now();
std::cout << "exception elapsed time: " << (end - start).count() << "sn";
start = std::chrono::system_clock::now();
for(size_t i = 11; i < 9999999; ++i)
return_optional_foo(i);
end = std::chrono::system_clock::now();
std::cout << "optional elapsed time: " << (end - start).count() << "sn";
start = std::chrono::system_clock::now();
for(size_t i = 11; i < 9999999; ++i)
exception_foo(i);
end = std::chrono::system_clock::now();
std::cout << "exception elapsed time: " << (end - start).count() << "sn";
return 0;
}
在我的 CentOS 上,使用 gcc 4.7,它的计时时间为:
[amit@amit tmp]$ ./a.out
code elapsed time: 39893s
exception elapsed time: 466762s
optional elapsed time: 215282s
exception elapsed time: 38436s
在原版设置中,以及:
[amit@amit tmp]$ ./a.out
code elapsed time: 0s
exception elapsed time: 238985s
optional elapsed time: 33595s
exception elapsed time: 24350
在 -O2 设置下。
附言我个人会使用异常/堆栈展开,因为我相信它是 C+ 的基本部分,可能正如上面所说的@vsoftco。
要回答你的问题,这不是一个荒谬的想法,你实际上可以使用std::exception
进行常规错误处理;有一些警告。
使用std::exception
作为函数的结果
假设一个函数可以在几种错误状态下退出:
std::exception f( int i )
{
if (i > 10)
return std::out_of_range( "Index is out of range" );
if ( can_do_f() )
return unexpected_operation( "Can't do f in the current state" );
return do_the_job();
}
如何使用std::exception
或可选选项来处理此问题?当函数返回时,将创建异常的副本,仅保留std::exception
部分并忽略实际错误的细节;留给你唯一的信息是"是的,出了点问题......"。该行为与返回布尔值或预期结果类型的可选值(如果有)相同。
使用std::exception_ptr
保存细节
另一种解决方案是应用与 std::p romise 相同的方法,即返回一个std::exception_ptr
。在那里,您将能够返回任何内容或异常,同时保留实际的错误详细信息。恢复错误的实际类型可能仍然很棘手。
在同一对象中返回错误或结果
最后,另一种选择是利用Expected<T>
提案及其实施。在那里,您将能够在单个对象中返回值或错误,并根据需要处理错误(通过测试错误或常规异常处理),对于函数不返回值的情况有一些特殊性(更多关于堆栈溢出或这个博客)。
如何选择
我个人对这个问题的看法是,如果你打算使用异常,那么按照它们的设计方式使用它们,最终使用一些额外的工具,如Expected<T>
,使它更容易。否则,如果您无法使用标准的异常处理,那么请选择已经证明自己的解决方案,例如经典的错误代码系统。
- 警告处理为错误这里有什么问题
- "error: no matching function for call to"构造函数错误
- boost::进程间消息队列引发错误
- C++,OpenCV,尝试显示图像时"OpenCV(4.3.0) Error: Assertion failed (size.width>0 && size.height>0)"此错误
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- QT在错误的班级中寻找空位
- vector.resize()中的分配错误
- 代码在main()中运行,但在函数中出现错误
- 释放错误后堆使用
- (C++)分析树以计算返回错误值的简单算术表达式
- Project Euler问题4的错误解决方案
- 我的字符计数代码计算错误.为什么
- 从"int*"强制转换为"unsigned int"会丢失精度错误
- 尝试导入pybind-opencv模块时出现libgtk错误
- 为什么与常规GCC不同,即使有"学究性错误",MinGW-GCC也能容忍丢失的返回类型
- C++错误LNK2005和不同项目文件的常规继承
- C++中的错误处理,构造函数与常规方法
- 我可以/应该使用 std::exception's 进行常规错误处理吗?
- 常规强制转换不会引发运行时错误
- 在 C++11 段错误中基于范围的 for 循环,但不是常规 for 循环