版权所有 © 2016-2021 Barrett Adair
根据 Boost 软件许可协议 1.0 版分发。(请参阅随附文件 LICENSE.md 或访问 https://boost.ac.cn/LICENSE_1_0.txt)
目录
Boost.CallableTraits
是一个 C++11 的仅头文件库,用于检查、合成和分解可调用类型。Boost.CallableTraits
旨在成为 C++17 提案 p0172 的最后一段中提到的 “用于函数类型的完整类型操作工具”,并消除了对不同函数签名进行模板特化的需求。如果可用,也支持 C++17 noexcept
和事务内存 TS。
#include <type_traits> #include <tuple> #include <boost/callable_traits.hpp> namespace ct = boost::callable_traits; // This function template helps keep our example code neat template<typename A, typename B> void assert_same(){ static_assert(std::is_same<A, B>::value, ""); } // foo is a function object struct foo { void operator()(int, char, float) const {} }; int main() { // Use args_t to retrieve a parameter list as a std::tuple: assert_same< ct::args_t<foo>, std::tuple<int, char, float> >(); // has_void_return lets us perform a quick check for a void return type static_assert(ct::has_void_return<foo>::value, ""); // Detect C-style variadics (ellipses) in a signature (e.g. printf) static_assert(!ct::has_varargs<foo>::value, ""); // pmf is a pointer-to-member function: void (foo::*)(int, char, float) const using pmf = decltype(&foo::operator()); // remove_member_const_t lets you remove the const member qualifier assert_same< ct::remove_member_const_t<pmf>, void (foo::*)(int, char, float) /*no const!*/ >(); // Conversely, add_member_const_t adds a const member qualifier assert_same< pmf, ct::add_member_const_t<void (foo::*)(int, char, float)> >(); // is_const_member_v checks for the presence of member const static_assert(ct::is_const_member<pmf>::value, ""); }
“不要尝试编写辅助代码来检测 PMF/PMD 并对其进行分派 -- 这绝对是一场噩梦。PMF 类型是核心语言中最糟糕的类型。”
-- Stephan T. Lavavej, CppCon 2015, "functional: What's New, And Proper Usage"
考虑一下下面的代码,它定义了 48 个模板特化,这些特化对于在纯 C++17 中为每个有效的函数类型进行特化是必要的
template<typename T> struct foo; template<class Return, class... Args> struct foo<Return(Args...)> {}; template<class Return, class... Args> struct foo<Return(Args...) &> {}; template<class Return, class... Args> struct foo<Return(Args...) &&> {}; template<class Return, class... Args> struct foo<Return(Args...) const> {}; template<class Return, class... Args> struct foo<Return(Args...) const &> {}; template<class Return, class... Args> struct foo<Return(Args...) const &&> {}; template<class Return, class... Args> struct foo<Return(Args...) volatile> {}; template<class Return, class... Args> struct foo<Return(Args...) volatile &> {}; template<class Return, class... Args> struct foo<Return(Args...) volatile &&> {}; template<class Return, class... Args> struct foo<Return(Args...) const volatile> {}; template<class Return, class... Args> struct foo<Return(Args...) const volatile &> {}; template<class Return, class... Args> struct foo<Return(Args...) const volatile &&> {}; template<class Return, class... Args> struct foo<Return(Args..., ...)> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) &> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) &&> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const &> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const &&> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) volatile> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) volatile &> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) volatile &&> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const volatile> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const volatile &> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const volatile &&> {}; template<class Return, class... Args> struct foo<Return(Args...) noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) && noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) const noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) const & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) const && noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) volatile noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) volatile & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) volatile && noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) const volatile noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) const volatile & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args...) const volatile && noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) && noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const && noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) volatile noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) volatile & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) volatile && noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const volatile noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const volatile & noexcept> {}; template<class Return, class... Args> struct foo<Return(Args..., ...) const volatile && noexcept> {};
对于成员函数指针、函数指针、函数引用、函数对象和 transaction_safe
,情况变得更加复杂。
诚然,在日常应用程序代码库中,这种晦涩的特化的用例几乎不存在。即使在库代码中,这些也极其罕见。然而,有一些元编程场景只能通过这种模板“垃圾信息”来解决。编写、测试和维护此类代码既乏味又昂贵。
Boost.CallableTraits
为这个问题提供了一个最终且决定性的库级解决方案,并完全消除了对这些特化的需求(平台特定的调用约定除外)。
Boost.CallableTraits
中的功能在很大程度上与 Boost.FunctionTypes
重叠。以下是您可能更喜欢 Boost.CallableTraits
的一些原因
Boost.FunctionTypes
与 Boost.MPL
序列紧密耦合,而 Boost.CallableTraits
除了标准库之外没有其他依赖项。Boost.CallableTraits
目标是 C++11 及更高版本Boost.CallableTraits
将函数对象/lambda 表达式视为一等公民。Boost.CallableTraits
支持左值/右值引用成员限定符。Boost.CallableTraits
支持 noexcept
和 transaction_safe
。Boost.FunctionTypes
不尝试将所有可调用类型分解为统一的、INVOKE
-感知接口。Boost.FunctionTypes
严重依赖“标签”类型,而 Boost.CallableTraits
遵循 <type_traits> 的风格。在 Boost.FunctionTypes
中支持 C++11 及更高版本将需要大量这些标签的扩散。例如,以下是如何在 Boost.FunctionTypes
库中从成员函数指针类型中删除成员 const
的方法
#include <type_traits> #include <boost/function_types/components.hpp> #include <boost/function_types/member_function_pointer.hpp> struct foo { void bar() const {} }; using const_removed = typename boost::function_types::member_function_pointer< typename boost::function_types::components<decltype(&foo::bar)>::types, boost::function_types::non_const>::type; static_assert(std::is_same<const_removed, void(foo::*)()>::value, ""); int main(){}
Boost.CallableTraits
使这更容易
#include <type_traits> #include <boost/callable_traits/remove_member_const.hpp> struct foo { void bar() const {} }; using const_removed = boost::callable_traits::remove_member_const_t<decltype(&foo::bar)>; static_assert(std::is_same<const_removed, void(foo::*)()>::value, ""); int main(){}
Boost.FunctionTypes
库包含一个出色的 示例,用于生成类型擦除接口(实现 在此)。此示例已使用 Boost.CallableTraits
重新实现,以产生 稍微更直观的接口。
Boost.FunctionTypes
是一个优秀的库,但其接口仍有改进空间。
Boost.CallableTraits
在 GCC 4.7.4+、Clang 3.5.2+、XCode 6.4+ 和 Visual Studio 2015+ 上受支持。Intel C++ 编译器未正式支持,尽管适用于 Linux 的 2017 版本确实通过了一些测试用例。
表 1. GCC 支持
功能 |
GCC 7.3.0 及更高版本 |
GCC 6.3.0 |
GCC 5.4.0 |
GCC 4.9.2 |
GCC 4.8.2 |
GCC 4.7.4 |
---|---|---|---|---|---|---|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (无 abominables) |
c++11 (无 abominables) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (无 abominables) |
c++11 (无 abominables) |
|
c++11 |
c++11 |
c++11 |
c++11 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
|
c++11 |
c++11 |
c++11 |
c++11 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (无 abominables) |
c++11 (无 abominables) |
|
c++17 |
c++17 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
|
c++17 (需要 -fgnu-tm) |
c++17 (需要 -fgnu-tm) |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (无 abominables) |
c++11 (无 abominables) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (无 abominables) |
c++11 (无 abominables) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (无 abominables) |
c++11 (无 abominables) |
|
c++11 |
c++11 |
c++11 |
c++11 |
未知 |
未知 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (始终为 false) |
c++11 (始终为 false) |
|
c++17 |
c++17 |
c++11 (始终为 false) |
c++11 (始终为 false) |
c++11 (始终为 false) |
c++11 (始终为 false) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (始终为 false) |
c++11 (始终为 false) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (始终为 false) |
c++11 (始终为 false) |
|
c++17 (需要 -fgnu-tm) |
c++17 (需要 -fgnu-tm) |
c++11 (始终为 false) |
c++11 (始终为 false) |
c++11 (始终为 false) |
c++11 (始终为 false) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (无 abominables) |
c++11 (无 abominables) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (无 abominables) |
c++11 (无 abominables) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (无 abominables) |
c++11 (无 abominables) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (无效果) |
c++11 (无效果) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 (无 abominables) |
c++11 (无 abominables) |
|
c++17 |
c++17 |
c++11 (无效果) |
c++11 (无效果) |
c++11 (无效果) |
c++11 (无效果) |
|
c++17 (需要 -fgnu-tm) |
c++17 (需要 -fgnu-tm) |
c++11 (无效果) |
c++11 (无效果) |
c++11 (无效果) |
c++11 (无效果) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
表 2. LLVM/Clang 支持
功能 |
Clang 4.0.0 及更高版本 |
Clang 3.8.0 |
Clang 3.7.1 |
Clang 3.6.2 |
Clang 3.5.2 |
---|---|---|---|---|---|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++17 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
|
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++17 |
c++11 (始终为 false) |
c++11 (始终为 false) |
c++11 (始终为 false) |
c++11 (始终为 false) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 (始终为 false) |
c++11 (始终为 false) |
c++11 (始终为 false) |
c++11 (始终为 false) |
c++11 (始终为 false) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++17 |
c++11 (无效果) |
c++11 (无效果) |
c++11 (无效果) |
c++11 (无效果) |
|
c++11 (无效果) |
c++11 (无效果) |
c++11 (无效果) |
c++11 (无效果) |
c++11 (无效果) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
表 3. XCode/AppleClang 支持
功能 |
XCode 8 及更高版本 |
XCode 7.3 |
XCode 7.2 |
XCode 7.1 |
XCode 6.4 |
---|---|---|---|---|---|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
未知 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
|
未知 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
未知 |
c++11 (始终为 false) |
c++11 (始终为 false) |
c++11 (始终为 false) |
c++11 (始终为 false) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
未知 |
c++11 (始终为 false) |
c++11 (始终为 false) |
c++11 (始终为 false) |
c++11 (始终为 false) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
未知 |
c++11 (无效果) |
c++11 (无效果) |
c++11 (无效果) |
c++11 (无效果) |
|
未知 |
c++11 (无效果) |
c++11 (无效果) |
c++11 (无效果) |
c++11 (无效果) |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
|
c++11 |
c++11 |
c++11 |
c++11 |
c++11 |
表 4. Visual Studio 支持
功能 |
MSVC with Visual Studio 2017 |
MSVC with Visual Studio 2015 (最新更新) |
---|---|---|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
|
static_assert 在实例化时失败 |
static_assert 在实例化时失败 |
|
c++11 (定义 BOOST_DISABLE_WIN32 以禁用 PMF varargs 上的 __cdecl) |
c++11 (定义 BOOST_DISABLE_WIN32 以禁用 PMF varargs 上的 __cdecl) |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 (定义 BOOST_DISABLE_WIN32 以禁用 PMF varargs 上的 __cdecl) |
c++11 (定义 BOOST_DISABLE_WIN32 以禁用 PMF varargs 上的 __cdecl) |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 |
c++11 (对于同时具有 ref 和 cv 限定符的函数,始终为 false,原因是编译器错误) |
|
c++11 |
c++11 |
|
c++11 (始终为 false) |
c++11 (始终为 false) |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 (始终为 false) |
c++11 (始终为 false) |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 |
c++11 |
|
c++11 (无效果) |
c++11 (无效果) |
|
c++11 (无效果) |
c++11 (无效果) |
|
c++11 (定义 BOOST_DISABLE_WIN32 以禁用 PMF varargs 上的 __cdecl) |
c++11 (定义 BOOST_DISABLE_WIN32 以禁用 PMF varargs 上的 __cdecl) |
|
c++11 |
c++11 |