查看 std::exception
的定义,除了析构函数,其他包括默认构造和拷贝构造在内的函数都声明为 noexcept
:
因为不允许复制
std::exception
抛出异常,故当派生类(例如std::runtime_error
)必须管理用户定义的诊断信息时,常将它实现为写时复制的字符串。
查看 std::logic_error
的定义 或 std::runtime_error
的定义:
因为不容许复制
std::logic_error
抛出异常,通常将此消息在内部存储为分离分配的引用计数字符串。这也是构造函数不接收std::string&&
参数的理由:无论如何它必须复制内容。
为什么
std::exception
拷贝构造不允许抛出异常?反证法。如果允许其构造抛出异常,异常又继承自
std::exception
,还是直接崩溃更合理。std::logic_error
等以字符串为入参的构造函数是可能抛出std::bad_alloc
异常的,为什么子类的拷贝构造不能抛出异常?不抛异常是理想状态,能够实现就尽力实现。
domain_error
overflow_error
和 underflow_error
,并未用于数学函数中
mathematical functions report domamin/overflow/underflow errors as specified in math_errhandling.
标准库里的异常都是按需建立的,大多服务于特定的函数或库。能够在项目中借鉴、使用的也就七八个类型:
logic_error
及其大部分子类:invalid_argument
,domain_error
,length_error
,out_of_range
runtime_error
及其部分子类:range_error
,overflow_error
,underflow_error
相同层级的各异常类型的使用场景并非严格分离的,多个异常的定义存在重复、交叉,在同一场景中往往存在多个异常都可以使用。比如,
- 用于线程库的
future_error
从logic_error
继承;但 - 用于正则表达式库的
regex_error
和用于格式化库的format_error
又是从runtime_error
继承;而 - 用于
optional
、variant
和any
等类型的 bad_xxx 这些又是直接从std::exception
继承 - 在函数
std::stoi
抛出out_of_range
的场景是不是更符合range_error
的定义
又比如:length_error
和 range_error
很像;invalid_argument
和 domain_error
重叠;
C++ When to use which (standard) exception?
异常继承体系最初区分 logic error 和 runtime error 就过于理想,最终还是要 服务于具体的某个函数或库 才有意义。
以下表格 摘自,并修正错误
异常 | 描述 |
---|---|
std::exception | 该异常是所有标准 C++ 异常的父类。 |
std::bad_alloc | 该异常可以通过 new 抛出。 |
std::bad_cast | 该异常可以通过 dynamic_cast 抛出。 |
std::bad_exception | 这在处理 C++ 程序中无法预期的异常时非常有用。 |
std::bad_typeid | 该异常可以通过 typeid 抛出。 |
std::logic_error | 理论上可以通过读取代码来检测到的异常。 |
std::domain_error | 当使用了一个无效的数学域时,会抛出该异常。 |
std::invalid_argument | 当使用了无效的参数时,会抛出该异常。 |
std::length_error | 当创建了太长的 std::string 时,会抛出该异常。 |
std::out_of_range | 该异常可以通过方法抛出,std::bitset<>::operator[]() |
std::runtime_error | 理论上不可以通过读取代码来检测到的异常。 |
std::overflow_error | 当发生数学上溢时,会抛出该异常。 |
std::range_error | 当尝试存储超出范围的值时,会抛出该异常。 |
std::underflow_error | 当发生数学下溢时,会抛出该异常。 |
跨线程使用异常
跨线程使用异常,优先使用 std::future
,或者请移步 std::exception_ptr
类型。
定义
std::future
使用了后者;而后者就是为跨线程捕获异常新增的特性。
logic_error
It reports errors that are a consequence of faulty logic within the program such as violating logical preconditions or class invariants and may be preventable.
No standard library components throw this exception directly
invalid_argument.
an argument value has not been accepted.
This exception is thrown by std::bitset::bitset
, and the std::stoi
and std::stof
families of functions.
domain_error
domain_error
, that is, situations where the inputs are outside of the domain on which an operation is defined.
The standard library components do not throw this exception。和 std::invalid_argument
的区别?
length_error
attempt to exceed/超出 implementation defined length limits for some object.
This exception is thrown by member functions of std::basic_string
and std::vector::reserve
out_of_range
attempt to access elements out of defined range.
It may be thrown by the member functions of std::bitset
and std::basic_string
, by std::stoi
and std::stod
families of functions, and by the bounds-checked member access functions (e.g. std::vector::at
and std::map::at
).
future_error(C++11)
that is thrown on failure by the functions in the thread library.
runtime_error
It reports errors that are due to events beyond the scope of the program and can not be easily predicted.
Exceptions of type std::runtime_error
are thrown by the following standard library components: std::locale::locale
and std::locale::combine
.
range_error
range errors (that is, situations where a result of a computation cannot be represented by the destination type).
The only standard library components that throw this exception are std::wstring_convert::from_bytes
and std::wstring_convert::to_bytes
.
template class std::wstring_convert
is deprecated in c++17.
overflow_error
to report arithmetic overflow errors (that is, situations where a result of a computation is too large for the destination type)
The only standard library components that throw this exception are std::bitset::to_ulong
and std::bitset::to_ullong
.
underflow_error
to report arithmetic underflow errors (that is, situations where the result of a computation is a subnormal floating-point value)
The standard library components do not throw this exception.
regex_error(C++11)
to report errors in the regular expressions library.
tx_exception(TM TS)
can be used to cancel and roll back an atomic transaction initiated by the keyword atomic_cancel
.
nonexistent_local_time(C++20)
to report that an attempt was made to convert a nonexistent std::chrono::local_time
to a std::chrono::sys_time
without specifying a std::chrono::choose
(such as choose::earliest
or choose::latest
).
This exception is thrown by std::chrono::time_zone::to_sys
and functions that call it (such as the constructors of std::chrono::zoned_time
that takes a std::chrono::local_time
).
Ambiguous and nonexistent local times can occur as a result of time zone transitions (such as daylight saving time/夏令时).
ambiguous_local_time(C++20)
to report that an attempt was made to convert a ambiguous std::chrono::local_time
to a std::chrono::sys_time
without specifying a std::chrono::choose
(such as choose::earliest
or choose::latest
).
This exception is thrown by std::chrono::time_zone::to_sys
and functions that call it (such as the constructors of std::chrono::zoned_time
that takes a std::chrono::local_time
).
Ambiguous and nonexistent local times can occur as a result of time zone transitions (such as daylight saving time/夏令时).
format_error(C++20)
to report errors in the formatting library.
system_error(C++11)
thrown by various library functions (typically the functions that interface with the OS facilities, e.g. the constructor of std::thread
) when the exception has an associated std::error_code
, which may be reported.
ios_base::failure(C++11)
is thrown on failure by the functions in the Input/Output library.
filesystem::filesystem_error(C++17)
is thrown on failure by the throwing overloads of the functions in the filesystem library.
1 | std::uintmax_t file_size( const std::filesystem::path& p ); |
bad_optional_access(C++17)
be thrown by std::optional::value
when accessing an optional object that does not contain a value.
bad_typeid
is thrown when a typeid
operator is applied to a dereferenced null pointer value of a polymorphic type.
多态,含有虚函数的类型。
std::nullptr_t
类型很奇特,它不是指针类型。即 std::nullptr_t p = nullptr;
对变量 *p
解引用是错误的。
bad_cast
is thrown when a dynamic_cast
to a reference type fails the run-time check (e.g. because the types are not related by inheritance), and also from std::use_facet
if the requested facet does not exist in the locale.
bad_any_cast(C++17)
be thrown by the value-returning forms of std::any_cast
on failure.
和类型 std::any
相关
bad_weak_ptr(C++11)
thrown by the constructors of std::shared_ptr
that take std::weak_ptr
as the argument, when the std::weak_ptr
refers to an already deleted object.
优先使用 std::weak_ptr<T>::lock
避免上述异常。
bad_function_call(C++11)
thrown by std::function::operator()
if the function wrapper has no target.
bad_alloc
thrown as exceptions by the allocation functions to report failure to allocate storage.
bad_array_new_length(C++11)
thrown as exceptions by the new-expressions to report invalid array lengths.
bad_exception
thrown by the C++ runtime in the following situations:
- If
std::exception_ptr
stores a copy of the caught exception and if the copy constructor of the exception object caught bystd::current_exception
throws an exception, the captured exception is an instance ofstd::bad_exception
.
bad_variant_access(C++17)
thrown in the following situations:
std::get(std::variant)
called with an index or type that does not match the currently active alternativestd::visit
called to visit a variant that is valueless_by_exception