模板元编程是一种元编程技术,其中编译器使用模板生成临时源代码,该代码由编译器与其余源代码合并然后编译。
我正在查看 libc++ 的代码,我注意到了这个片段: // __指针 _LIBCPP_ALLOCATOR_TRAITS_HAS_XXX(__has_pointer, 指针); 模板 我正在查看 libc++ 的代码,我注意到了这个片段: // __pointer _LIBCPP_ALLOCATOR_TRAITS_HAS_XXX(__has_pointer, pointer); template <class _Tp, class _Alloc, class _RawAlloc = __libcpp_remove_reference_t<_Alloc>, bool = __has_pointer<_RawAlloc>::value> struct __pointer { using type _LIBCPP_NODEBUG = typename _RawAlloc::pointer; }; template <class _Tp, class _Alloc, class _RawAlloc> struct __pointer<_Tp, _Alloc, _RawAlloc, false> { using type _LIBCPP_NODEBUG = _Tp*; }; 让我疑惑的是这一行: bool = __has_pointer<_RawAlloc>::value 这段代码的语义非常清楚:这里我们调用一个元函数__has_pointer,如果它为真,我们就使用__pointer的第一个实现,如果不是,我们就使用第二个(显式为假的那个)在其模板参数中)。我很困惑,因为我不明白这是如何工作的,第一个实例不应该在其模板参数中有一个明确的true然后模板专业化开始吗?如果是这样,那么我们需要在启动此模板时调用元函数,所以也许bool = __has_pointer<_RawAlloc>::value>是它的简写?我想知道这里使用了什么样的机制才能允许这样做。用于使这项工作的规则是什么? _LIBCPP_ALLOCATOR_TRAITS_HAS_XXX的完整实现可以在这里找到: #define _LIBCPP_ALLOCATOR_TRAITS_HAS_XXX(NAME, PROPERTY) \ template <class _Tp, class = void> struct NAME : false_type { }; \ template <class _Tp> struct NAME<_Tp, __void_t<typename _Tp:: PROPERTY > > : true_type { } 第一个实例不应该在其模板参数中有一个明确的 true 然后模板专业化开始吗? 首先,您定义一个模板。如果您尝试实例化一个模板,就会得到这个“基本”模板。 然后你可以定义一个专业化。如果模板参数与专业化匹配,那么您将获得专业化模板。惊喜! template <class _Tp, class _Alloc, class _RawAlloc = __libcpp_remove_reference_t<_Alloc>, bool = __has_pointer<_RawAlloc>::value> struct __pointer 让我们忽略专业化。如果没有专门化,这是您在实例化此模板时将获得的模板。这就是当最后一个模板参数是 true 或 false 时得到的结果。没关系。这是你的模板。好好享受。祝你胃口好 但是等等,你忘了:你也有专长。别急:如果最后一个模板参数是false,你的餐点就是专业化。 但是,如果最后一个模板参数原来是true没有任何变化,你仍然得到原始模板。其他地方都不需要设置为“显式真实”。 话虽如此,是的,您实际上可以通过 two 专业化,甚至不定义基本模板,使事情看起来像这样: template <class _Tp, class _Alloc, class _RawAlloc = __libcpp_remove_reference_t<_Alloc>, bool = __has_pointer<_RawAlloc>::value> struct __pointer; template <class _Tp, class _Alloc, class _RawAlloc> struct __pointer<_Tp, _Alloc, _RawAlloc, true> { using type _LIBCPP_NODEBUG = typename _RawAlloc::pointer; }; template <class _Tp, class _Alloc, class _RawAlloc> struct __pointer<_Tp, _Alloc, _RawAlloc, false> { using type _LIBCPP_NODEBUG = _Tp*; }; 这在逻辑上是等价的。这两种选择在逻辑上是等价的。 这只是一个类型特征,用于使用类的部分专业化 分派到特定的实现。有 template <class _Tp, class _Alloc, class _RawAlloc = __libcpp_remove_reference_t<_Alloc>, bool = __has_pointer<_RawAlloc>::value> struct __pointer { using type _LIBCPP_NODEBUG = typename _RawAlloc::pointer; }; 定义了一个初级类模板,模板的bool = __has_pointer<_RawAlloc>::value部分是一个bool非类型tmplate参数,默认为__has_pointer<_RawAlloc>::value的值。 __has_pointer<_RawAlloc>::value 将返回 true 如果 _RawAlloc 有一个 pointer 成员和 false 否则。 接下来我们有 template <class _Tp, class _Alloc, class _RawAlloc> struct __pointer<_Tp, _Alloc, _RawAlloc, false> { using type _LIBCPP_NODEBUG = _Tp*; }; 这是主模板的偏特化,只要最后一个模板参数是false.就会被使用 这意味着当 _RawAlloc 有 pointer 成员时使用主模板,而当它没有时使用专业化
为 integral_constant 添加特化的程序的行为是未定义的
我不熟悉在 C++ 中使用模板元编程。 我希望能够将整数和类型名的混合传递给可变参数模板。我无法同时指定两者,所以我使用 typename...pack...
为 integral_constant 添加特化的程序的 C++ 行为是未定义的
我不熟悉在 C++ 中使用模板元编程。 我希望能够将整数和类型名的混合传递给可变参数模板。我无法同时指定两者,所以我使用 typename...pack....
我对奇怪的重复模板有以下问题,当我尝试访问 CRTP 基类的数据成员时出现问题。 模板 结构基础{ 内部保护...
我想根据模板参数的变量数量的类型生成一个字符串文字。每种类型都应翻译成字符串文字(1 个或多个字符),然后是文字 ...
我需要定义一个枚举 枚举类 MyEnum { MyEnum1 = 0x0001, MyEnum2 = 0x0002, MyEnum3 = 0x0004, MyEnum4 = 0x0008, MyEnum5 = 0x0010 }; 所以基本上这些枚举值是由
我想在可变模板类中创建一个函数类型声明。该函数需要接受模板参数列表对应的参数。我想弄清楚是否...
这段检测默认运算符的代码为什么从来不选择特化部分是有原因的吗?
此代码始终打印 0,我不明白为什么。我错过了什么吗? #include #include 模板 结构
检查是否可以计算 lambda constexpr (C++17)
我正在尝试找到一种方法来检查 lambda 是否可以持续评估或 不在 C++17 中。假设我要检查的 lambda 不带 参数。 我偶然发现 这 问题,谁接受...
对于实现条件类型,我非常喜欢 std::conditional_t,因为它使代码简短且可读性强: 模板 使用 bit_type = std::conditional_t 对于实现条件类型,我非常喜欢std::conditional_t,因为它使代码简短且可读性强: template<std::size_t N> using bit_type = std::conditional_t<N == std::size_t{ 8 }, std::uint8_t, std::conditional_t<N == std::size_t{ 16 }, std::uint16_t, std::conditional_t<N == std::size_t{ 32 }, std::uint32_t, std::conditional_t<N == std::size_t{ 64 }, std::uint64_t, void>>>>; 使用起来非常直观: bit_type<8u> a; // == std::uint8_t bit_type<16u> b; // == std::uint16_t bit_type<32u> c; // == std::uint32_t bit_type<64u> d; // == std::uint64_t 但由于这是一个纯条件类型,因此必须有一个默认类型 - void,在这种情况下。因此,如果 N 是任何其他值,则该类型产生: bit_type<500u> f; // == void 现在不能编译了,但是 yielding 类型仍然有效。 意味着你可以说 bit_type<500u>* f; 并且会有一个有效的程序! 那么有没有什么好的方法可以让遇到条件类型的fail case时编译失败呢? 立即有一个想法是将最后一个std::conditional_t替换为std::enable_if_t: template<std::size_t N> using bit_type = std::conditional_t<N == std::size_t{ 8 }, std::uint8_t, std::conditional_t<N == std::size_t{ 16 }, std::uint16_t, std::conditional_t<N == std::size_t{ 32 }, std::uint32_t, std::enable_if_t< N == std::size_t{ 64 }, std::uint64_t>>>>; 问题是模板总是被完全评估,这意味着 std::enable_if_t 总是被完全评估 - 如果 N != std::size_t{ 64 } 就会失败。呃 我目前的解决方法是相当笨拙地引入一个结构和 3 using 声明: template<std::size_t N> struct bit_type { private: using vtype = std::conditional_t<N == std::size_t{ 8 }, std::uint8_t, std::conditional_t<N == std::size_t{ 16 }, std::uint16_t, std::conditional_t<N == std::size_t{ 32 }, std::uint32_t, std::conditional_t<N == std::size_t{ 64 }, std::uint64_t, void>>>>; public: using type = std::enable_if_t<!std::is_same_v<vtype, void>, vtype>; }; template<std::size_t N> using bit_type_t = bit_type<N>::type; static_assert(std::is_same_v<bit_type_t<64u>, std::uint64_t>, ""); 这通常有效,但我不喜欢它,因为它添加了太多东西,我还不如使用模板专业化。它还将 void 保留为特殊类型 - 因此它在 void 实际上是分支的收益的情况下不起作用。有可读的、简短的解决方案吗? 你可以通过添加一个间接级别来解决这个问题,这样最外层的 conditional_t 的结果就不是一个类型,而是一个需要 ::type 才能应用到它的元函数。然后使用 enable_if 而不是 enable_if_t 这样您就不会访问 ::type 除非确实需要它: template<typename T> struct identity { using type = T; }; template<std::size_t N> using bit_type = typename std::conditional_t<N == std::size_t{ 8 }, identity<std::uint8_t>, std::conditional_t<N == std::size_t{ 16 }, identity<std::uint16_t>, std::conditional_t<N == std::size_t{ 32 }, identity<std::uint32_t>, std::enable_if<N == std::size_t{ 64 }, std::uint64_t>>>>::type; 在此版本中,最终分支中的类型是 enable_if<condition, uint64_t>,它始终是有效类型,并且只有在实际采用该分支并且需要 enable_if<false, uint64_t>::type 时才会出现错误。当采用较早的分支之一时,您最终使用 identity<uintNN_t>::type 作为一种较小的整数类型,并且 enable_if<false, uint64_t> 没有嵌套类型(因为您不使用它)并不重要。 只是为了好玩......使用std::tuple和std::tuple_element完全避免std::conditional怎么样? 如果您可以使用 C++14(因此模板变量和模板变量的特化),您可以编写一个模板变量用于转换大小/元组中的索引 template <std::size_t> constexpr std::size_t bt_index = 100u; // bad value template <> constexpr std::size_t bt_index<8u> = 0u; template <> constexpr std::size_t bt_index<16u> = 1u; template <> constexpr std::size_t bt_index<32u> = 2u; template <> constexpr std::size_t bt_index<64u> = 3u; 所以bit_type变成 template <std::size_t N> using bit_type = std::tuple_element_t<bt_index<N>, std::tuple<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>>; 如果你只能使用 C++11,你可以开发一个 bt_index() constexpr 返回正确(或不正确)值的函数。 您可以验证是否满意 static_assert( std::is_same_v<bit_type<8u>, std::uint8_t>, "!" ); static_assert( std::is_same_v<bit_type<16u>, std::uint16_t>, "!" ); static_assert( std::is_same_v<bit_type<32u>, std::uint32_t>, "!" ); static_assert( std::is_same_v<bit_type<64u>, std::uint64_t>, "!" ); 以及使用 bit_type 和不受支持的尺寸 bit_type<42u> * pbt42; 导致编译错误。 -- 编辑 -- 正如 Jonathan Wakely 所建议的,如果你可以使用 C++20,那么 std::has_single_bit()(以前是 std::ispow2())和 std::bit_width()(以前是 std::log2p1()),你可以简化很多:你可以完全避免bt_index而简单地写 template <std::size_t N> using bit_type = std::tuple_element_t<std::has_single_bit(N) ? std::bit_width(N)-4u : -1, std::tuple<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>>;
所以我正在尝试创建一个类型特征来说明两个“外部”类类型是否相同。 IE。 std::vector 与 std::vector 相同,我不关心任何内部参数...
假设我有这个: 模板 结构A { A() { 标准::计算<< W << " - " << H << std::endl; } }; And I also have another s...
在 C++ 中,创建一个验证某个类具有特定方法的概念,问题是该方法可以接受任意数量的参数
我正在尝试创建一个概念来验证类是否具有特定方法。该约束应仅检查具有此类名称的方法是否存在以及特定返回类型。它不应该放
我正在尝试将数组签名扩展为 size_t 模板参数包。 我正在使用“现代模板元编程:纲要”幻灯片 24 中的示例,(Walter Brown,CppCo ...
我正在尝试从容器中提取值类型。事实证明,大多数 std 容器都有一个 value_type,但不幸的是,maps 将它用于其他目的。 所以,我试图创建一个代码...
我有一个包含多个模板方法的类,即: 类 MessageEndpoint { 民众: 使用 SupportedMessages = boost::mp11::mp_list 我有一个包含多个模板方法的类,即: class MessageEndpoint { public: using SupportedMessages = boost::mp11::mp_list<messaging::MessageType1, messaging::MessageType2, messaging::MessageTypeN>; public: virtual ~MessageEndpoint(); public: template <typename MessageType> UID subscribe(const UID &publisher_uid, std::function<void(const MessageType &)> callback); template <typename MessageType> void send_message(const MessageType &message); template <typename MessageType> void send_message(MessageType &&message); } 必须为几种类型预实例化这些方法(在SupportedMessages列表中)。 当然,我可以为列表中的每个类做这样的事情: // Instantiation. template <> UID subscribe<messaging::MessageType1>(const UID &publisher_uid, std::function<void(const messaging::SpikeMessage &)> callback); template <> void MessageEndpoint::send_message<messaging::MessageType1>(messaging::MessageType1 &&message); template <> void MessageEndpoint::send_message<messaging::MessageType1>(const messaging::MessageType1 &message); 但这又长又丑。我该怎么做,像这样: template_for<MessageEndpoint::SupportedMessages, T, { template <> UID subscribe<T>(const UID &publisher_uid, std::function<void(const T &)> callback); template <> void MessageEndpoint::send_message<T>(T &&message); template <> void MessageEndpoint::send_message<T>(const T &message); }> 这可能吗?我如何使用 Boost::MP11 做到这一点? 我找到了一个可接受的解决方案。 一些部分解决方案(感谢@davis-herring 和@quxflux): 为一组类型专门化许多模板. 一组类型的模板专业化. 条件: 我们不能用标量替换模板,因为我们有很多组合。即使我们有两种类型。 我们不能在另一个代码的编译过程中实例化模板。 IE。这是 Python 中使用的库。 我们不想在添加新类型时总是手动创建新实例。 有关实例化的所有信息必须在编译时已知。 没有用于创建“实例化循环”的 C++ 语言结构。 所以只有一种方法可以解决这个问题:使用预处理器。 方便的方法是使用 Boost.Preprocessor library. 我将展示简化示例:实体关系基础实现。 这个例子可能不会编译,但它所基于的真实代码是有效的。 namespace my_code { // There are comma separated types. // I.e. declared somewhere in the type traits library. // These will be used by preprocessor macroses. // MUST be declared without parentheses. #define ALL_ENTITIES EntityType1, EntityType2, EntityTypeN // MP11 list creation: for example, how to work with types lists. using AllEntites = boost::mp11::mp_list<ALL_ENTITIES>; #define ALL_RELATIONS RelationOneToMany, RelationOneToOne using AllRelations = boost::mp11::mp_list<ALL_RELATIONS>; class ER { public: // Some metaprogramming stuff. using AllEntityContainers = boost::mp11::mp_transform<EntityContainer, AllEntites>; using AllRelationContainers = boost::mp11::mp_transform<RelationContainer, AllRelations>; using AllEntityVariants = boost::mp11::mp_rename<AllEntityContainers, std::variant>; using AllRelationVariants = boost::mp11::mp_rename<AllRelationContainers, std::variant>; public: using EntityContainer = std::vector<AllEntityVariants>; using RelationContainer = std::vector<AllRelationVariants>; public: // Templates must be instantiated explicitly. template <typename EntityType> void add_entity(EntityType &&entity); template <typename EntityType> EntityType &get_entity(const UID &entity_uid); template <typename EntityType> const EntityType &get_entity(const UID &entity_uid) const; public: // Another templates group must be instantiated explicitly. template <typename RelationType> void add_relation(RelationType &&relation); template <typename RelationType> RelationType &get_relation(const UID &relation_uid); template <typename RelationType> const RelationType &get_relation(UID &relation_uid) const; private: template <typename T, typename VT> typename std::vector<VT>::iterator find_elem(const knp::core::UID &uid, std::vector<VT> &container); private: EntityContainer entities_; RelationContainer relations_; }; } // namespace my_code. 实施: namespace my_code { // This template used in another template methods and will be instantiated automatically. template <typename T, typename VT> typename std::vector<VT>::iterator ER::find_elem(const UID &uid, std::vector<VT> &container) { auto result = std::find_if( container.begin(), container.end(), [&uid](VT &p_variant) -> bool { constexpr auto type_n = boost::mp11::mp_find<VT, T>(); if (p_variant.index() != type_n) return false; return uid == (std::get<type_n>(p_variant)).get_uid(); }); return result; } // Must be instantiated explicitly. template <typename EntityType> void ER::add_entity(EntityType &&entity) { entities_.emplace_back(ER::AllEntitiesVariants(entity)); } // Must be instantiated explicitly. template <typename EntityType> EntityType &ER::get_entity(const UID &entity_uid) { auto r = find_elem<EntityType, AllEntityVariants>(entity_uid, entities_); if (r != entities_.end()) return std::get<EntityType>(*r); throw std::runtime_error("Can't find entity!"); } // Must be instantiated explicitly. template <typename EntityType> const EntityType &ER::get_entity(const UID &entity_uid) const { return const_cast<ER*>(this)->get_entity<EntityType>(entity_uid); } // Must be instantiated explicitly. void ER::add_relation(ER::AllRelationVariants &&relation) { relations_.emplace_back(relation); } // Must be instantiated explicitly. template <typename RelationType> void ER::add_relation(RelationType &&relation) { add_relation(ER::AllRelationVariants(relation)); } // Must be instantiated explicitly. template <typename RelationType> RelationType &ER::get_relation(const UID &relation_uid) { auto r = find_elem<RelationType, AllRelationVariants>(relation_uid, relations_); if (r != relations_.end()) return std::get<RelationType>(*r); throw std::runtime_error("Can't find relation!"); } // Must be instantiated explicitly. template <typename RelationType> const RelationType &ER::get_relation(const UID &relation_uid) const { return const_cast<ER *>(this)->get_relation<RelationType>(relation_uid); } // Entity methods instantiation macro, which will be called in cycle // by preprocessor. #define INSTANCE_ENTITY_FUNCTIONS(n, template_for_instance, neuron_type) \ template void ER::add_entity<Entity<entity_type>>(Entity<entity_type> &&); \ template Entity<entity_type> &ER::get_entity<Entity<entity_type>>(const UID &); \ template const Entity<entity_type> &ER::get_entity<Entity<entity_type>>(const knp::core::UID &) const; // Relation methods instantiation macro, which will be called in cycle // by preprocessor. #define INSTANCE_RELATION_FUNCTIONS(n, template_for_instance, relation_type) \ template void ER::add_relation<Relation<relation_type>>(Relation<relation_type> &&); \ template Relation<relation_type> &ER::get_relation<Relation<relation_type>>(const UID &); \ template const Relation<relation_type> &ER::get_relation<Relation<relation_type>>(const UID &) const; // Entities instantiation cycle. BOOST_PP_SEQ_FOR_EACH(INSTANCE_ENTITY_FUNCTIONS, "", BOOST_PP_VARIADIC_TO_SEQ(ALL_ENTITIES)) // Relations instantiation cycle. BOOST_PP_SEQ_FOR_EACH(INSTANCE_RELATION_FUNCTIONS, "", BOOST_PP_VARIADIC_TO_SEQ(ALL_RELATIONS)) } // namespace my_code 这段代码对我来说已经足够了,但如果有人愿意,他可以使用这种“技术”做出更复杂的东西。 例如,BOOST_PP_SEQ_FOR_EACH_PRODUCT可以用来做几个类的所有组合列表(有些问题是必须要解决的,即相等的类组合实例化几次,但这是可能的)。
我如何创建一个基于 std::variant 的函数数组,它由一些消息类型组成,它们将从 io 字节中解码,以便我可以快速访问正确的函数...
在 C# 和 Java 领域工作多年后,我最近又回到了 C++ 领域,并且很喜欢 C++ 在我缺席的情况下的发展方向(从 C++11 开始,现在正在学习 C++20!)。模板的力量真的是
我正试图从模板元编程开始。我写了这样的简单构造:模板 Class Fact { public: enum { result = N * Fact ::result }; }; template& ...
模板 C fnc(); 模板<> int fnc(){return 0;}模板。 C var; template <> int var = 0; /编译错误 int main() { }有一个特殊化的...