异常 std::exception

查看 std::exception 的定义,除了析构函数,其他包括默认构造和拷贝构造在内的函数都声明为 noexcept

因为不允许复制 std::exception 抛出异常,故当派生类(例如 std::runtime_error )必须管理用户定义的诊断信息时,常将它实现为写时复制的字符串。

查看 std::logic_error 的定义std::runtime_error 的定义:

因为不容许复制 std::logic_error 抛出异常,通常将此消息在内部存储为分离分配的引用计数字符串。这也是构造函数不接收 std::string&& 参数的理由:无论如何它必须复制内容。

  1. 为什么 std::exception 拷贝构造不允许抛出异常?

    反证法。如果允许其构造抛出异常,异常又继承自 std::exception,还是直接崩溃更合理。

  2. std::logic_error 等以字符串为入参的构造函数是可能抛出 std::bad_alloc 异常的,为什么子类的拷贝构造不能抛出异常?

    不抛异常是理想状态,能够实现就尽力实现。

domain_error overflow_errorunderflow_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_errorlogic_error 继承;但
  • 用于正则表达式库的 regex_error 和用于格式化库的 format_error 又是从 runtime_error 继承;而
  • 用于 optionalvariantany 等类型的 bad_xxx 这些又是直接从 std::exception 继承
  • 在函数 std::stoi 抛出 out_of_range 的场景是不是更符合 range_error 的定义

又比如:length_errorrange_error 很像;invalid_argumentdomain_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::vector 和 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
2
std::uintmax_t file_size( const std::filesystem::path& p );
std::uintmax_t file_size( const std::filesystem::path& p, std::error_code& ec ) noexcept;

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 by std::current_exception throws an exception, the captured exception is an instance of std::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 alternative
  • std::visit called to visit a variant that is valueless_by_exception