C++17标准为预处理器增加了一个新特性__has_include,用于判断某个头文件是否能够被包含进来,例如:
#if __has_include(<optional>)
# include <optional>
# define have_optional 1
#elif __has_include(<experimental/optional>)
# include <experimental/optional>
# define have_optional 1
# define experimental_optional 1
#else
# define have_optional 0
#endif如果__has_include(<optional>)中的头文件optional可以被包含进来,那么表达式求值为1;否则求值为0。请注意,__has_include的实参必须和#include的实参具有同样的形式,否则会导致编译错误。另外,__has_include并不关心头文件是否已经被包含进来。
C++20标准添加了一组用于测试功能特性的宏,这组宏可以帮助我们测试当前的编译环境对各种功能特性的支持程度。
属性测试宏(__has_cpp_attribute)可以指示编译环境是否支持某种属性,该属性可以是标准属性,也可以是编译环境厂商特有的属性。标准属性将被展开为该属性添加到标准时的年份和月份,而厂商特有的属性将被展开为一个非零的值:
std::cout << __has_cpp_attribute(deprecated); // 输出结果如下:201309上面这句代码会输出201309,代表该属性在2013年9月加入标准,并且被当前编译环境支持。当前的标准属性如表32-1所示。
▼表32-1
属性 | 值 |
|---|---|
carries_dependency | 200809L |
deprecated | 201309L |
fallthrough | 201603L |
likely | 201803L |
maybe_unused | 201603L |
no_unique_address | 201803L |
nodiscard | 201603L |
noreturn | 200809L |
unlikely | 201803L |
以下列表的宏代表编译环境所支持的语言功能特性,每个宏将被展开为该特性添加到标准时的年份和月份。请注意,这些宏展开的值会随着特性的变更而更新,如表32-2所示。
▼表32-2
宏 | 值 |
|---|---|
__cpp_aggregate_bases | 201603L |
__cpp_aggregate_nsdmi | 201304L |
__cpp_aggregate_paren_init | 201902L |
__cpp_alias_templates | 200704L |
__cpp_aligned_new | 201606L |
__cpp_attributes | 200809L |
__cpp_binary_literals | 201304L |
__cpp_capture_star_this | 201603L |
__cpp_char8_t | 201811L |
__cpp_concepts | 201907L |
__cpp_conditional_explicit | 201806L |
__cpp_consteval | 201811L |
__cpp_constexpr | 201907L |
__cpp_constexpr_dynamic_alloc | 201907L |
__cpp_constexpr_in_decltype | 201711L |
__cpp_constinit | 201907L |
__cpp_coroutines | 201902L |
__cpp_decltype | 200707L |
__cpp_decltype_auto | 201304L |
__cpp_deduction_guides | 201907L |
__cpp_delegating_constructors | 200604L |
__cpp_designated_initializers | 201707L |
__cpp_enumerator_attributes | 201411L |
__cpp_fold_expressions | 201603L |
__cpp_generic_lambdas | 201707L |
__cpp_guaranteed_copy_elision | 201606L |
__cpp_hex_float | 201603L |
__cpp_if_constexpr | 201606L |
__cpp_impl_coroutine | 201902L |
__cpp_impl_destroying_delete | 201806L |
__cpp_impl_three_way_comparison | 201907L |
__cpp_inheriting_constructors | 201511L |
__cpp_init_captures | 201803L |
__cpp_initializer_lists | 200806L |
__cpp_inline_variables | 201606L |
__cpp_lambdas | 200907L |
__cpp_modules | 201907L |
__cpp_namespace_attributes | 201411L |
__cpp_noexcept_function_type | 201510L |
__cpp_nontype_template_args | 201911L |
__cpp_nontype_template_parameter_auto | 201606L |
__cpp_nsdmi | 200809L |
__cpp_range_based_for | 201603L |
__cpp_raw_strings | 200710L |
__cpp_ref_qualifiers | 200710L |
__cpp_return_type_deduction | 201304L |
__cpp_rvalue_references | 200610L |
__cpp_sized_deallocation | 201309L |
__cpp_static_assert | 200410L |
__cpp_structured_bindings | 201606L |
__cpp_template_template_args | 201611L |
__cpp_threadsafe_static_init | 200806L |
__cpp_unicode_characters | 200704L |
__cpp_unicode_literals | 200710L |
__cpp_user_defined_literals | 200809L |
__cpp_using_enum | 201907L |
__cpp_variable_templates | 201304L |
__cpp_variadic_templates | 200704L |
__cpp_variadic_using | 201611L |
以下列表的宏代表编译环境所支持的标准库功能特性,它们通常包含在<version>头文件或者表中的任意对应头文件中。同样,每个宏将被展开为该特性添加到标准时的年份和月份。请注意,这些宏展开的值会随着特性的变更而更新,如表32-3所示。
▼表32-3
宏 | 值 | 头文件 |
|---|---|---|
__cpp_lib_addressof_constexpr | 201603L | <memory> |
__cpp_lib_allocator_traits_is_always_equal | 201411L | <memory> <scoped_allocator> <string> <deque> <forward_list> <list> <vector> <map><set> <unordered_map> <unordered_set> |
__cpp_lib_any | 201606L | <any> |
__cpp_lib_apply | 201603L | <tuple> |
__cpp_lib_array_constexpr | 201811L | <iterator> <array> |
__cpp_lib_as_const | 201510L | <utility> |
__cpp_lib_assume_aligned | 201811L | <memory> |
__cpp_lib_atomic_flag_test | 201907L | <atomic> |
__cpp_lib_atomic_float | 201711L | <atomic> |
__cpp_lib_atomic_is_always_lock_free | 201603L | <atomic> |
__cpp_lib_atomic_lock_free_type_aliases | 201907L | <atomic> |
__cpp_lib_atomic_ref | 201806L | <atomic> |
__cpp_lib_atomic_shared_ptr | 201711L | <memory> |
__cpp_lib_atomic_value_initialization | 201911L | <atomic> <memory> |
__cpp_lib_atomic_wait | 201907L | <atomic> |
__cpp_lib_barrier | 201907L | <barrier> |
__cpp_lib_bind_front | 201907L | <functional> |
__cpp_lib_bit_cast | 201806L | <bit> |
__cpp_lib_bitops | 201907L | <bit> |
__cpp_lib_bool_constant | 201505L | <type_traits> |
__cpp_lib_bounded_array_traits | 201902L | <type_traits> |
__cpp_lib_boyer_moore_searcher | 201603L | <functional> |
__cpp_lib_byte | 201603L | <cstddef> |
__cpp_lib_char8_t | 201907L | <atomic> <filesystem> <istream> <limits><locale> <ostream> <string> <string_view> |
__cpp_lib_chrono | 201907L | <chrono> |
__cpp_lib_chrono_udls | 201304L | <chrono> |
__cpp_lib_clamp | 201603L | <algorithm> |
__cpp_lib_complex_udls | 201309L | <complex> |
__cpp_lib_concepts | 202002L | <concepts> |
__cpp_lib_constexpr_algorithms | 201806L | <algorithm> |
__cpp_lib_constexpr_complex | 201711L | <complex> |
__cpp_lib_constexpr_dynamic_alloc | 201907L | <memory> |
__cpp_lib_constexpr_functional | 201907L | <functional> |
__cpp_lib_constexpr_iterator | 201811L | <iterator> |
__cpp_lib_constexpr_memory | 201811L | <memory> |
__cpp_lib_constexpr_numeric | 201911L | <numeric> |
__cpp_lib_constexpr_string | 201907L | <string> |
__cpp_lib_constexpr_string_view | 201811L | <string_view> |
__cpp_lib_constexpr_tuple | 201811L | <tuple> |
__cpp_lib_constexpr_utility | 201811L | <utility> |
__cpp_lib_constexpr_vector | 201907L | <vector> |
__cpp_lib_coroutine | 201902L | <coroutine> |
__cpp_lib_destroying_delete | 201806L | <new> |
__cpp_lib_enable_shared_from_this | 201603L | <memory> |
__cpp_lib_endian | 201907L | <bit> |
__cpp_lib_erase_if | 202002L | <string> <deque> <forward_list> <list> <vector> <map> <set> <unordered_map> <unordered_set> |
__cpp_lib_exchange_function | 201304L | <utility> |
__cpp_lib_execution | 201902L | <execution> |
__cpp_lib_filesystem | 201703L | <filesystem> |
__cpp_lib_format | 201907L | <format> |
__cpp_lib_gcd_lcm | 201606L | <numeric> |
__cpp_lib_generic_associative_lookup | 201304L | <map> <set> |
__cpp_lib_generic_unordered_lookup | 201811L | <unordered_map> <unordered_set> |
__cpp_lib_hardware_interference_size | 201703L | <new> |
__cpp_lib_has_unique_object_representations | 201606L | <type_traits> |
__cpp_lib_hypot | 201603L | <cmath> |
__cpp_lib_incomplete_container_elements | 201505L | <forward_list> <list> <vector> |
__cpp_lib_int_pow2 | 202002L | <bit> |
__cpp_lib_integer_comparison_functions | 202002L | <utility> |
__cpp_lib_integer_sequence | 201304L | <utility> |
__cpp_lib_integral_constant_callable | 201304L | <type_traits> |
__cpp_lib_interpolate | 201902L | <cmath> <numeric> |
__cpp_lib_invoke | 201411L | <functional> |
__cpp_lib_is_aggregate | 201703L | <type_traits> |
__cpp_lib_is_constant_evaluated | 201811L | <type_traits> |
__cpp_lib_is_final | 201402L | <type_traits> |
__cpp_lib_is_invocable | 201703L | <type_traits> |
__cpp_lib_is_layout_compatible | 201907L | <type_traits> |
__cpp_lib_is_nothrow_convertible | 201806L | <type_traits> |
__cpp_lib_is_null_pointer | 201309L | <type_traits> |
__cpp_lib_is_pointer_interconvertible | 201907L | <type_traits> |
__cpp_lib_is_swappable | 201603L | <type_traits> |
__cpp_lib_jthread | 201911L | <stop_token> <thread> |
__cpp_lib_latch | 201907L | <latch> |
__cpp_lib_launder | 201606L | <new> |
__cpp_lib_list_remove_return_type | 201806L | <forward_list> <list> |
__cpp_lib_logical_traits | 201510L | <type_traits> |
__cpp_lib_make_from_tuple | 201606L | <tuple> |
__cpp_lib_make_reverse_iterator | 201402L | <iterator> |
__cpp_lib_make_unique | 201304L | <memory> |
__cpp_lib_map_try_emplace | 201411L | <map> |
__cpp_lib_math_constants | 201907L | <numbers> |
__cpp_lib_math_special_functions | 201603L | <cmath> |
__cpp_lib_memory_resource | 201603L | <memory_resource> |
__cpp_lib_node_extract | 201606L | <map> <set> <unordered_map> <unordered_set> |
__cpp_lib_nonmember_container_access | 201411L | <iterator> <array> <deque> <forward_list> <list> <map> <regex> <set> <string> <unordered_map> <unordered_set> <vector> |
__cpp_lib_not_fn | 201603L | <functional> |
__cpp_lib_null_iterators | 201304L | <iterator> |
__cpp_lib_optional | 201606L | <optional> |
__cpp_lib_parallel_algorithm | 201603L | <algorithm> <numeric> |
__cpp_lib_polymorphic_allocator | 201902L | <memory> |
__cpp_lib_quoted_string_io | 201304L | <iomanip> |
__cpp_lib_ranges | 201911L | <algorithm> <functional> <iterator> <memory> <ranges> |
__cpp_lib_raw_memory_algorithms | 201606L | <memory> |
__cpp_lib_remove_cvref | 201711L | <type_traits> |
__cpp_lib_result_of_sfinae | 201210L | <type_traits> <functional> |
__cpp_lib_robust_nonmodifying_seq_ops | 201304L | <algorithm> |
__cpp_lib_sample | 201603L | <algorithm> |
__cpp_lib_scoped_lock | 201703L | <mutex> |
__cpp_lib_semaphore | 201907L | <semaphore> |
__cpp_lib_shared_mutex | 201505L | <shared_mutex> |
__cpp_lib_shared_ptr_arrays | 201707L | <memory> |
__cpp_lib_shared_ptr_weak_type | 201606L | <memory> |
__cpp_lib_shared_timed_mutex | 201402L | <shared_mutex> |
__cpp_lib_shift | 201806L | <algorithm> |
__cpp_lib_smart_ptr_for_overwrite | 201811L | <memory> |
__cpp_lib_source_location | 201907L | <source_location> |
__cpp_lib_span | 202002L | <span> |
__cpp_lib_ssize | 201902L | <iterator> |
__cpp_lib_starts_ends_with | 201711L | <string> <string_view> |
__cpp_lib_string_udls | 201304L | <string> |
__cpp_lib_string_view | 201803L | <string> <string_view> |
__cpp_lib_syncbuf | 201803L | <syncstream> |
__cpp_lib_three_way_comparison | 201907L | <compare> |
__cpp_lib_to_address | 201711L | <memory> |
__cpp_lib_to_array | 201907L | <array> |
__cpp_lib_to_chars | 201611L | <charconv> |
__cpp_lib_transformation_trait_aliases | 201304L | <type_traits> |
__cpp_lib_transparent_operators | 201510L | <memory> <functional> |
__cpp_lib_tuple_element_t | 201402L | <tuple> |
__cpp_lib_tuples_by_type | 201304L | <tuple> <utility> |
__cpp_lib_type_identity | 201806L | <type_traits> |
__cpp_lib_type_trait_variable_templates | 201510L | <type_traits> |
__cpp_lib_uncaught_exceptions | 201411L | <exception> |
__cpp_lib_unordered_map_try_emplace | 201411L | <unordered_map> |
__cpp_lib_unwrap_ref | 201811L | <functional> |
__cpp_lib_variant | 201606L | <variant> |
__cpp_lib_void_t | 201411L | <type_traits> |
从C99标准开始,C语言引入了可变参数宏__VA_ARGS__,而顺理成章的C++11标准也将其纳入标准当中。__VA_ARGS__常见的用法集中于打印日志上,例如:
#define LOG(msg, …) printf("[" __FILE__ ":%d] " msg, __LINE__, __VA_ARGS__)
LOG("Hello %d", 2020);LOG的使用和printf非常相似,并且可以很方便地将代码文件和行数记录到日志当中。不过它们也并非完全相同,因为对于函数printf来说,除了第一个参数以外的其他参数都是可选的:
printf("Hello 2020"); // 编译成功而对于LOG宏来说,这种写法是非法的:
LOG("Hello 2020");上面这句代码展开后应该是:
printf("[" __FILE__ ":%d] " "Hello 2020", __LINE__, );很明显,函数的最后多出了一个逗号,在GCC和CLang上编译都会报错。虽然在Visual Studio 2019上不会报错,但这并不具备通用性。实际上GCC和CLang也有类似扩展,只不过没有隐式地提供,我们可以使用##连接逗号和__VA_ARGS__:
#define LOG(msg, …) printf("[" __FILE__ ":%d] " msg, __LINE__, ##__VA_ARGS__)
LOG("Hello 2020"); // GCC和CLang编译成功为了用更加标准的方法解决以上问题,C++20标准引入了一个新的宏__VA_OPT__令可变参数宏更易于在可变参数为空的情况下使用。还是以刚刚的LOG为例,我们将代码修改为:
#define LOG(msg, …) printf("[" __FILE__ ":%d] " msg, __LINE__ __VA_OPT__ (,) __VA_ARGS__)观察上面的代码可以发现,__LINE__后面的逗号被修改为__VA_OPT__(,),这是告诉编译器这个逗号是可选的。当可变参数不为空的时候逗号才会被替换出来,否则就会忽略这个逗号。对于下面两句日志而言:
LOG("Hello 2020");
LOG("Hello %d", 2020);由于第一句代码中没有可变参数,所以被忽略,替换结果如下:
printf("[" __FILE__ ":%d] " "Hello 2020", __LINE__);第二句代码中存在可变参数,所以被替换出来,最后结果如下:
printf("[" __FILE__ ":%d] " "Hello %d", __LINE__, 2020);本章介绍了新标准增加的预处理器和宏,其中最主要的是特性测试宏。因为随着近十年来的C++标准更新速度的加快,在C++98标准沉浸十几年的C++程序员似乎并没有习惯标准这样的迭代速度,所以对编译环境支持哪些特性并没有深刻的认识。但是从C++20标准开始,我们可以通过这些宏来判断开发环境对新特性的支持程度,让程序员可以合理地利用更加优秀的C++特性。另外,对于代码库的作者也有不凡的意义,因为有了特性测试宏,他们可以根据客户端开发环境适配不同的功能代码,让自己的代码库能够高效地应用在更多的环境上。