列举C++元编程的优缺点以及通过Meta Prorammin编程思想让其变得简单可应用

注脚

展开查看详情

1.Modern C++元编程应用 祁宇 qicosmos@163.com purecpp.org 1

2. 2018 中国C++大会—purecpp.org 模版元 2

3.2018 中国C++大会—purecpp.org 3

4.2018 中国C++大会—purecpp.org 4

5. 2018 中国C++大会—purecpp.org !! 模版元 代码墙!! 在中国C++大会上?? 5

6. 2018 中国C++大会—purecpp.org 元编程的缺点 ● 代码晦涩 ● 比较难写 ● 编译时间长 ● 糟糕的错误提示 6

7. 2018 中国C++大会—purecpp.org 元编程的优点 ● zero-overhead 编译期计算 ● 简洁而优雅地解决问题 ● 终极抽象 Dream code! 7

8.2018 中国C++大会—purecpp.org 8

9. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● In C++98 ○ 元函数 ○ SFINAE ○ 模版递归 ○ 递归继承 ○ Tag Dispatch ○ 模版特化/偏特化 9

10. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● In C++98 template<class T> struct add_pointer { typedef T* type; }; typedef typename add_pointer<int>::type int_pointer; 元函数:编译期函数调用的类或模版类– add_pointer 调用元函数:访问元函数的内部类型::type Type T作为元函数的value,类型是元编程中的一等公民 模版元编程概念上是函数式编程 10

11. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● In C++98 template<bool B, class T = void> struct enable_if {}; template<class T> struct enable_if<true, T> { typedef T type; }; template <class T> typename enable_if<sizeof(T)==4, void>::type foo(T t) {} foo(1); //ok foo('a'); //compile error SFINAE:替换失败不是一个错误,基于模版实例化的tag dispatch 11

12. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● In C++98 template <int n> struct fact98 { static const int value = n * fact98<n - 1>::value; }; template <> struct fact98<0> { static const int value = 1; }; std::cout << fact98<5>::value << std::endl; 模版递归 12

13. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● In C++98 模版元编程集大成者:boost.mpl, boost.fusion boost.mpl:编译期类型容器和算法 boost.fusion:通过异构的编译期容器融合编译期和运行期计算 struct print{ template <typename T> void operator()(T const& x) const{ std::cout << typeid(x).name() << std::endl; } }; template <typename Sequence> void print_names(Sequence const& seq){ for_each(filter_if<boost::is_class<_> >(seq), print()); } boost::fusion::vector<int, char, std::string> stuff(2018, 'i', "purecpp"); print_names(stuff); 13

14. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● In C++98 Andrei Alexandrescu 14

15. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● In C++98 template<class T> struct add_pointer { typedef T* type; }; typedef typename add_pointer<int>::type int_pointer; ● In C++11 template<class T> using add_pointer = T*; using int_pointer = add_pointer<int>; C++11 template aliases(模版别名) 元函数由类和类模版变为模版别名 15

16. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● In C++11 template<typename... Values> struct meta_list {}; using list_of_ints = meta_list<char, short, int, long>; template<class List> struct list_size; template<template<class...> class List, class... Elements> struct list_size<List<Elements...>> : std::integral_constant<std::size_t, sizeof...(Elements)> {}; constexpr auto size = list_size<std::tuple<int, float, void>>::value; constexpr auto size1 = list_size<list_of_ints>::value; constexpr auto size2 = list_size<boost::variant<int, float>>::value; C++11 Variadic Templates Variadic templates作为类型容器 通过variadic templates pack访问模版参数,不通过模版递归和特化 16

17. 2018 中国C++大会—purecpp.org Meta Programming编程思想 C++11 type_traits提供了大量的元函数,让模版元编程变得更简单 17

18. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● In C++98 template <int n> struct fact98 { static const int value = n * fact98<n - 1>::value; }; template <> struct fact98<0> { static const int value = 1; }; std::cout << fact98<5>::value << std::endl; ● In C++11 constexpr int fact11(int n) { return n <= 1 ? 1 : (n * fact11(n - 1)); } 18

19. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● In C++11 constexpr int fact11(int n) { return n <= 1 ? 1 : (n * fact11(n - 1)); } ● In C++14 constexpr int fact14(int n) { int s = 1; for (int i = 1; i <= n; i++) { s = s * i; } return s; } 19

20. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● In C++14 Everything changed in meta programming 新特性产生新的编程思想! constexpr, generic lambda, variable template Boost.hana Louis Dionne 20

21. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● Boost.hana template <typename T> struct type_wrapper { using type = T; }; template <typename T> type_wrapper<T> type{}; //type to value auto the_int_type = type<int>; //value to type using the_real_int_type = decltype(the_int_type)::type;

22. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● Boost.hana template <typename T> type_wrapper<T> type{}; constexpr auto add_pointer = [](auto t) { using T = typename decltype(t)::type; return type<std::add_pointer_t<T>>; //type to value }; constexpr auto intptr = add_pointer(type<int>); static_assert(std::is_same_v<decltype(intptr)::type, int*>); //value to type 元函数的定义不再是类了,而是lambda! generic lambda, variable template, constexpr functional programming

23. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● Boost.hana auto animal_types = hana::make_tuple(hana::type_c<Fish*>, hana::type_c<Cat&>, hana::type_c<Dog*>); auto animal_ptrs = hana::filter(animal_types, [](auto a) { return hana::traits::is_pointer(a); }); static_assert(animal_ptrs == hana::make_tuple(hana::type_c<Fish*>, hana::type_c<Dog*>), ""); auto animals = hana::make_tuple(Fish{ "Nemo" }, Cat{ "Garfield" }, Dog{ "Snoopy" }); auto names = hana::transform(animals, [](auto a) { return a.name; }); assert(hana::reverse(names) == hana::make_tuple("Snoopy", "Garfield", "Nemo")); 通过类型容器融合编译期和运行期计算,替代boost.mpl和boost.fusion!

24. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● Boost.hana ○ 元函数不再是类或类模版,而是lambda ○ 不再基于类型,而是基于值 ○ 没有SFINAE,没有模版递归 ○ 函数式编程 ○ 代码更容易理解 ○ 元编程变得更简单 ○ 融合编译期与运行期

25. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● In C++14 template<std::size_t I> auto& get(person& p); In C++17 template<> auto& get<0>(person& p) { template<std::size_t I> return p.id; auto& get(person& p) { } if constexpr (I == 0) { return p.id; template<> } auto& get<1>(person& p) { else if constexpr (I == 1) { return p.name; return p.name; } } else if constexpr (I == 2) { template<> return p.age; auto& get<2>(person& p) { } return p.age; } } 没有特化 25

26. 2018 中国C++大会—purecpp.org Meta Programming编程思想 ● In C++14 template <typename T> std::enable_if_t<std::is_same_v<T, std::string>, std::string> to_string(T t){ return t; } template <typename T> std::enable_if_t<!std::is_same_v<T, std::string>, std::string> to_string(T t){ return std::to_string(t); } ● In C++17 template <typename T> std::string to_string(T t){ if constexpr(std::is_same_v<T, std::string>) return t; else return std::to_string(t); } No SFINAE 26

27. 2018 中国C++大会—purecpp.org C++新标准新特性产生新的idea,让元编程变得更简单更强大 variable templates constexpr generic lambda SFINAE template aliases constexpr recursive variadic templates tag dispatch C++14 specialize C++11 C++98 Newer is Better! ● C++98: boost.mpl, boost.fusion ● C++11: boost.mp11, meta, brigand ● C++14: boost.hana 27

28.2018 中国C++大会—purecpp.org 28

29.静态检查 29

30. 2018 中国C++大会—purecpp.org 静态检查 static_assert(sizeof(void *) == 8, "expected 64-bit platform"); template<typename T, int Row, int Column> struct Matrix { static_assert(Row >= 0, "Row number must be positive."); static_assert(Row >= 0, "Column number must be positive."); static_assert(Row + Column > 0, "Row and Column must be greater than 0."); }; 30

31. 2018 中国C++大会—purecpp.org 静态检查 struct A { void foo(){} int member; }; template<typename Function> std::enable_if_t<!std::is_member_function_pointer_v<Function>> foo(Function&& f) { } foo([] {}); //ok foo(&A::foo); //compile error: no matching function for call to 'foo(void (A::*)())' 安全,尽可能早地发现bug,让编译器而不是人帮助发现bug 31

32.静态检查 编译期探测 32

33. 2018 中国C++大会—purecpp.org 探测 template< class, class = void > struct has_foo : std::false_type {}; template< class T > struct has_foo< T, std::void_t<decltype(std::declval<T>().foo())> > : std::true_type {}; template< class, class = void > struct has_member : std::false_type {}; template< class T > struct has_member< T, std::void_t<decltype(std::declval<T>().member)> > : std::true_type {}; struct A { void foo(){} int member; decltype, void_t, SFINAE In C++17 }; static_assert(has_foo< A >::value); static_assert(has_member< A >::value); 33

34. 2018 中国C++大会—purecpp.org 探测 ● AOP(Aspect Oriented Programming) Core business Non-core business aspects 34

35. 2018 中国C++大会—purecpp.org Before aspects After aspects request Core response check log log check aspect aspect business function aspect aspect 35

36. 2018 中国C++大会—purecpp.org 探测 ● AOP constexpr bool has_befor_mtd = has_before<decltype(item), request&, response&>::value; if constexpr (has_befor_mtd) r = item.before(req, res); constexpr bool has_after_mtd = has_after<decltype(item), request&, response&>::value; if constexpr (has_after_mtd) r = item.after(req, res); 36

37. 2018 中国C++大会—purecpp.org 探测 ● AOP(Aspect Oriented Programming) #define HAS_MEMBER(member)\ template<typename T, typename... Args>\ struct has_##member\ {\ private:\ template<typename U> static auto Check(int) -> decltype(std::declval<U>().member(std::declval<Args>()...), std::true_type()); \ template<typename U> static std::false_type Check(...);\ public:\ enum{value = std::is_same<decltype(Check<T>(0)), std::true_type>::value};\ }; HAS_MEMBER(before) HAS_MEMBER(after) AOP in feather : https://github.com/qicosmos/feather 37

38.静态检查 编译期探测 编译期计算 38

39. 2018 中国C++大会—purecpp.org 编译期计算 ● 类型计算 ● 类型推导 ● 类型萃取 ● 类型转换 ● 数值计算 表达式模版,Xtensor, Eigen, Mshadow 39

40. 2018 中国C++大会—purecpp.org 类型萃取 template<typename Ret, typename... Args> struct function_traits_impl<Ret(Args...)>{ public: enum { arity = sizeof...(Args) }; typedef Ret function_type(Args...); typedef Ret result_type; using stl_function_type = std::function<function_type>; typedef Ret(*pointer)(Args...); template<size_t I> struct args{ static_assert(I < arity, "index is out of range, index must less than sizeof Args"); using type = typename std::tuple_element<I, std::tuple<Args...>>::type; }; typedef std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...> tuple_type; using args_type_t = std::tuple<Args...>; }; function_traits in cinatra: https://github.com/qicosmos/cinatra 40

41. 2018 中国C++大会—purecpp.org 类型萃取 ● rpc路由 struct rpc_service { int add(int a, int b) { return a + b; } std::string translate(const std::string& orignal) { std::string temp = orignal; for (auto& c : temp) c = toupper(c); return temp; } }; rpc_server server; server.register_handler("add", &rpc_service::add, &rpc_srv); server.register_handler("translate", &rpc_service::translate, &rpc_srv); auto result = client.call<int>("add", 1, 2); auto result = client.call<std::string>("translate", "hello"); Modern C++ rpc库rest_rpc: https://github.com/qicosmos/rest_rpc 41

42. 2018 中国C++大会—purecpp.org 类型萃取 ● 路由 template<typename Function> void register_nonmember_func(std::string const& name, const Function& f) { this->map_invokers_[name] = { std::bind(&invoker<Function>::apply, f, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) }; } template<typename Function> struct invoker { static void apply(const Function& func, const char* data, size_t size, std::string& result) { using args_tuple = typename function_traits<Function>::args_tuple; msgpack_codec codec; auto tp = codec.unpack<args_tuple>(data, size); call(func, result, tp); } }; 42

43. 2018 中国C++大会—purecpp.org 类型萃取 ● 路由 template<typename F, size_t... I, typename... Args> typename std::result_of<F(Args...)>::type call_helper( const F& f, const std::index_sequence<I...>&, const std::tuple<Args...>& tup) { return f(std::get<I>(tup)...); } template<typename F, typename... Args> typename std::enable_if<std::is_void<typename std::result_of<F(Args...)>::type>::value>::type call(const F& f, std::string& result, std::tuple<Args...>& tp) { call_helper(f, std::make_index_sequence<sizeof...(Args)>{}, tp); result = msgpack_codec::pack_args_str(result_code::OK); } 43

44.静态检查 编译期探测 编译期计算 编译期反射 44

45. 2018 中国C++大会—purecpp.org 编译期反射 ● 编译期反射 struct person{ std::string name; int age; }; REFLECTION(person, name, age) reflection reflection 45

46. 2018 中国C++大会—purecpp.org 编译期反射 ● 序列化引擎 person p = {"tom", 20}; iguana::string_stream ss; ● ORM ● 协议适配器 to_xml(ss, p); to_json(ss, p); to_msgpack(ss, p); to_protobuf(ss, p); ormpp::dbng<mysql> mysql; ormpp:: dbng<sqlite> sqlite; ormpp:: dbng<postgresql> postgres; mysql.create_datatable<person>() sqlite.create_datatable<person>() postgres.create_datatable<person>() 基于编译期反射的序列化引擎iguana: https://github.com/qicosmos/iguana 基于编译期反射的ORM库ormpp: https://github.com/qicosmos/ormpp 46

47.静态检查 编译期探测 编译期计算 编译期反射 融合编译期和运行期 47

48. 2018 中国C++大会—purecpp.org 融合编译期和运行期 ● 从运行期到编译期 Value to Type auto val = std::integral_constant<int, 5>{}; using int_type = decltype(val); ● 从编译期到运行期 Type to Value auto v = decltype(val)::value; 48

49. 2018 中国C++大会—purecpp.org 融合编译期和运行期 编译期 运行期 49

50. 2018 中国C++大会—purecpp.org 融合编译期和运行期 ● 如何根据一个运行时的值调用一个编译期模版函数? ● 如何将运行时的网络数据映射为一个函数调用? 50

51. 2018 中国C++大会—purecpp.org 融合编译期和运行期 template<size_t N> void foo(int n) { void fun() {} switch (n){ foo(100)?? case 0: void foo(int n) { fun<0>(); switch (n){ break; case 0: case 1: fun<0>(); fun<1>(); break; break; case 1: case 2: fun<1>(); fun<2>(); break; break; case 2: …… fun<2>(); case 99: break; fun<99>(); default: break; break; default: } break; } } } 51

52. 2018 中国C++大会—purecpp.org 融合编译期和运行期 namespace detail { template <class Tuple, class F, std::size_t...Is> void tuple_switch(const std::size_t i, Tuple&& t, F&& f, std::index_sequence<Is...>) { (void)std::initializer_list<int> { (i == Is && ( (void)std::forward<F>(f)(std::integral_constant<size_t, Is>{}), 0))... }; } } // namespace detail template <class Tuple, class F> inline void tuple_switch(const std::size_t i, Tuple&& t, F&& f) { constexpr auto N = std::tuple_size<std::remove_reference_t<Tuple>>::value; detail::tuple_switch(i, std::forward<Tuple>(t), std::forward<F>(f), std::make_index_sequence<N>{}); } 52

53. 2018 中国C++大会—purecpp.org 融合编译期和运行期 void foo(int n) { std::tuple<int, int, int> tp; tuple_switch(n, tp, [](auto item) { constexpr auto I = decltype(item)::value; fun<I>(); }); } foo(1); foo(2); foo(100)?? 53

54. 2018 中国C++大会—purecpp.org 融合编译期和运行期 template<size_t... Is> auto make_tuple_from_sequence(std::index_sequence<Is...>)->decltype(std::make_tuple(Is...)) { std::make_tuple(Is...); } template<size_t N> constexpr auto make_tuple_from_sequence()- >decltype(make_tuple_from_sequence(std::make_index_sequence<N>{})) { return make_tuple_from_sequence(std::make_index_sequence<N>{}); } void foo(int n) { decltype(make_tuple_from_sequence<100>()) tp; //std::tuple<int, int, …, int> tuple_switch(n, tp, [](auto item) { constexpr auto I = decltype(item)::value; fun<I>(); }); } foo(98); foo(99); 54

55. 2018 中国C++大会—purecpp.org 融合编译期和运行期 ● std::tuple:编译期和运行期的桥梁 ○ 类型容器 ○ 异构的值容器 ○ 通过遍历方式同时获得值与类型 ● std::index_sequence, std::integral_constant ○ 帮助遍历类型和值 ○ 帮助获得编译期索引 55

56.静态检查 编译期探测 编译期计算 编译期反射 融合编译期和运行期 接口泛化与统一 56

57. 2018 中国C++大会—purecpp.org 接口泛化与统一 ● 融合底层异构的子系统 ● 屏蔽差异 ● 提供统一的接口 ORM mysql postgresql sqlite 57

58. 2018 中国C++大会—purecpp.org 接口泛化与统一 ● Mysql connect mysql_real_connect(handle, "127.0.0.1", "feather", "2018", "testdb", 0, nullptr, 0); ● Postgresql connect PQconnectdb("host=localhost user=127.0.0.1 password=2018 dbname=testdb"); ● Sqlite connect sqlite3_open("testdb", handle); ● ORM unified connect interface ORM::mysql.connect("127.0.0.1", “feather", “2018", "testdb"); ORM::postgres.connect("127.0.0.1", “feather", “2018", "testdb"); ORM::sqlite.connect("testdb"); 58

59. 2018 中国C++大会—purecpp.org 接口泛化与统一 ● 通过可变参数模板统一接口 ● 通过policy-base设计和variadic templates来屏蔽数据库接口差异 template<typename DB> class dbng{ template <typename... Args> bool connect(Args&&... args){ return db_.connect(std::forward<Args>(args)...); } template<typename... Args> bool connect(Args... args) { if constexpr (sizeof...(Args)==5) { return std::apply(&mysql_real_connect, std::make_tuple(args...); } else if constexpr (sizeof...(Args) == 4) {//postgresql} else if constexpr (sizeof...(Args) == 2) {//sqlite} } 59

60. 2018 中国C++大会—purecpp.org 接口泛化与统一 if constexpr + variadic templates = 静态多态 ● 通过增加参数或修改参数类型方式来扩展接口 ● 没有继承 ● 没有SFINAE ● 没有模版特化 Modern C++ ORM库: https://github.com/qicosmos/ormpp 60

61.静态检查 编译期探测 编译期计算 编译期反射 融合编译期和运行期 接口泛化与统一 消除重复(宏) 61

62. 2018 中国C++大会—purecpp.org 消除重复(宏) #define ENUM_TO_OSTREAM_FUNC(EnumType) \ std::ostream& operator<<(std::ostream& out_stream, const EnumType& x) { \ out_stream << static_cast<int>(x); \ return out_stream; \ } enum class MsgType { Connected, Timeout }; enum class DataType {Float, Int32}; ENUM_TO_OSTREAM_FUNC(MsgType); ENUM_TO_OSTREAM_FUNC(DataType); std::stringstream ss; ss << MsgType::Connected<< DataType::Float; 62

63. 2018 中国C++大会—purecpp.org 消除重复(宏) #define ENUM_TO_OSTREAM_FUNC(EnumType) \ std::ostream& operator<<(std::ostream& out_stream, const EnumType& x) { \ out_stream << static_cast<int>(x); \ return out_stream; \ } enum class MsgType { Connected, Timeout }; enum class DataType {Float, Int32}; ENUM_TO_OSTREAM_FUNC(MsgType); ENUM_TO_OSTREAM_FUNC(DataType); template<typename T, typename = typename std::enable_if<std::is_enum<T>::value>::type> std::ostream& operator<<(std::ostream& out_stream, T x) { out_stream << static_cast<int>(x); return out_stream; } 63

64. 2018 中国C++大会—purecpp.org 消除重复(宏) #define CALL(name, ...) \ do { \ result ret = func(name); \ if (ret == 0) { \ __VA_ARGS__; \ do_something(name); \ }\ else { \ do_something (name); \ }\ } while (0) CALL("root", func1(root_path)); CALL("temp", func2(temp_path)); 64

65. 2018 中国C++大会—purecpp.org 消除重复(宏) template<typename Self, typename F> void Call(const std::string& name, Self * self, F f) { auto ret = foo(name); if (ret == 0) { (self >*f)(name); do_something(name); } else { do_something(name); } } 大部分宏能做的,元编程能做得更好,更完美! 65

66.静态检查 编译期探测 编译期计算 编译期反射 融合编译期和运行期 接口泛化与统一 消除重复(宏) 接口易用性和灵活性 66

67. 2018 中国C++大会—purecpp.org 接口易用和灵活性 struct dummy{ int add(connection* conn, int a, int b) { return a + b; } }; int add(connection* conn, int a, int b) { return a + b; } rpc_server server(8080, 4); dummy d; server.register_handler("a", &dummy::add, &d); server.register_handler("b", add); server.register_handler("c", [](connection* conn) {}); server.register_handler("d", [](connection* conn, std::string s) { return s; }); 同一个接口可以注册任意类型函数(callable) 67

68. 2018 中国C++大会—purecpp.org 接口易用和灵活性 ● 类型擦除 template<typename Function > 擦除 void register_nonmember_func(std::string const& name, const Function& f) { this->map_invokers_[name] = { std::bind(&invoker<Function>::apply, f, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) }; } template<typename Function> struct invoker { static void apply(const Function& func, const char* data, size_t size, std::string& result) { using args_tuple = typename function_traits<Function>::args_tuple; 还原 msgpack_codec codec; auto tp = codec.unpack<args_tuple>(data, size); call(func, result, tp); } }; https://github.com/qicosmos/rest_rpc 68

69. 2018 中国C++大会—purecpp.org 接口易用和灵活性 struct person{ void foo(request& req, response& res) { res.render_string("ok"); } }; server.set_http_handler<GET>("/a", &person::foo); ● Play game! 69

70. 2018 中国C++大会—purecpp.org 接口易用和灵活性 server.set_http_handler<GET>("/a", &person::foo); server.set_http_handler<GET, POST>("/b", &person::foo, log_t{}); server.set_http_handler<GET, POST, HEAD>("/c", &person::foo, log_t{}, check{}); server.set_http_handler<GET>("/d", &person::foo, log_t{}, check{}, enable_cache{ false }); server.set_http_handler<GET>("/e", &person::foo, log_t{}, enable_cache{ false }, check{}); server.set_http_handler<POST>("/f", &person::foo, enable_cache{ false }, log_t{}, check{}); 70

71. 2018 中国C++大会—purecpp.org 接口易用和灵活性 template<http_method... Is, typename Function, typename... AP> void set_http_handler(std::string_view name, Function&& f, AP&&... ap) { if constexpr(has_type<enable_cache<bool>, std::tuple<std::decay_t<AP>...>>::value) { auto tp = filter<enable_cache<bool>>(std::forward<AP>(ap)...); std::apply(f, std::move(tp)); } else { http_router_.register_handler<Is...>(name, std::forward<Function>(f), std::forward<AP>(ap)...); } } template <typename T, typename Tuple> struct has_type; template <typename T, typename... Us> struct has_type<T, std::tuple<Us...>> : std::disjunction<std::is_same<T, Us>...> {}; 71

72. 2018 中国C++大会—purecpp.org 接口易用和灵活性 template< typename T> struct filter_helper{ static constexpr auto func(){ return std::tuple<>(); } template< class... Args > static constexpr auto func(T&&, Args&&...args){ return filter_helper::func(std::forward<Args>(args)...); } template< class X, class... Args > static constexpr auto func(X&&x, Args&&...args){ return std::tuple_cat(std::make_tuple(std::forward<X>(x)), filter_helper::func(std::forward<Args>(args)...)); } }; 72

73.静态检查 编译期探测 编译期计算 编译期反射 融合编译期和运行期 接口泛化与统一 消除重复(宏) 接口易用性和灵活性 使用元编程 73

74. Thank you! Code: https://github.com/qicosmos qicosmos@163.com purecpp.org Newer is Better! 74