【问题标题】:Deduction guide to extract template template from types从类型中提取模板模板的演绎指南
【发布时间】:2019-09-23 14:43:52
【问题描述】:

考虑以下类:

// Class definition
template <template <class...> class... Templates>
class template_pack
{
    public:
    template <class... Types>
    constexpr template_pack(const Types&...) noexcept;
};

// Class template argument deduction guide
template <class... Types>
template_pack(const Types&...) -> template_pack</* something here */>

我们假设Types... 的格式为template &lt;class...&gt; class... Templates。我想要的是:

template_pack pack(std::vector<int>{}, std::list<double>{}, std::deque<char>{});

导致:

template_pack<std::vector, std::list, std::deque>;

如何做到这一点?

【问题讨论】:

  • 不确定这是否可能,因为从技术上讲,没有像 std::vector, etc 这样的类。只有std::vector&lt;type&gt;。不过,我可能是错的,或者它可能在新的 C++ 中发生了变化(我习惯于 C++11)。

标签: c++ c++17 template-meta-programming template-argument-deduction template-templates


【解决方案1】:

如何做到这一点?

我没有办法:总有一些东西是无法推断出来的。

不完全是你问的,但我能想象的最好的通过自定义类型特征ttw(用于“模板模板包装器”)

template <template <typename...> class C>
struct ttw
 { 
   template <typename ... Ts>
   constexpr ttw (C<Ts...> const &) 
    { }
 };

使用隐式推导指南,从构造函数接收的类型中提取模板模板并将其用作模板参数。

所以你可以用接收ttw&lt;Templates&gt;的构造函数编写template_pack

template <template <typename...> class... Templates>
struct template_pack
 {
   constexpr template_pack (ttw<Templates> const & ...)
    { }
 };

您可以按如下方式使用(再次:通过隐式演绎指南)

template_pack tp1 {ttw{std::vector<int>{}},
                   ttw{std::set<long>{}},
                   ttw{std::map<char, short>{}}};

问题是有必要在ttw{} 中显式包装参数,因为举个例子,std::vector&lt;int&gt; 可以转换为ttw&lt;std::vector&gt;,但不是ttw&lt;std::vector&gt;。因此,通过std::vector{} 而不是ttw{std::vector{}},我们遇到了通常无法推断的类型的鸡/蛋问题,因为要推断它,需要进行转换,该转换需要了解我们想要推断的类型。

显然,您可以要求明确的 ttw 包装适用于特定的 make_template_pack() 函数

template <typename ... Ts>
constexpr auto make_template_pack (Ts && ... ts)
 { return template_pack{ttw{std::forward<Ts>(ts)}...}; }

以下是完整的编译示例

#include <map>
#include <set>
#include <vector>
#include <type_traits>

template <template <typename...> class C>
struct ttw
 { 
   template <typename ... Ts>
   constexpr ttw (C<Ts...> const &) 
    { }
 };

template <template <typename...> class... Templates>
struct template_pack
 {
   constexpr template_pack (ttw<Templates> const & ...)
    { }
 };

template <typename ... Ts>
constexpr auto make_template_pack (Ts && ... ts)
 { return template_pack{ttw{std::forward<Ts>(ts)}...}; }

int main ()
 { 
   template_pack tp1 {ttw{std::vector<int>{}},
                      ttw{std::set<long>{}},
                      ttw{std::map<char, short>{}}};

   auto tp2 { make_template_pack(std::vector<long>{},
                                 std::set<int>{},
                                 std::map<char, short>{}) };

   using t0 = template_pack<std::vector, std::set, std::map>;
   using t1 = decltype(tp1);
   using t2 = decltype(tp2);

   static_assert( std::is_same<t0, t1>::value );
   static_assert( std::is_same<t0, t2>::value );
 }

【讨论】:

    【解决方案2】:

    我“成功”了其他特质:

    template <typename T> struct template_traits;
    
    // Variadic case
    template <template <class...> class C, typename ... Ts>
    struct template_traits<C<Ts...>>
    {
        template <typename ... Us>
        using template_type = C<Us...>;
    };
    

    然后,论据推演为:

    // Class template argument deduction guide
    template <class... Types>
    template_pack(Types&&...)
    -> template_pack<template_traits<std::decay_t<Types>>::template template_type...>;
    

    Demo

    问题是别名并不完全相同(gcc 认为某些别名是相同的 与clang相反)(我为那个BTW开了一个question

    template_traits&lt;std::vector&gt;::template_type 不是std::vector,即使对于任何TAtemplate_traits&lt;std::vector&gt;::template_type&lt;T, A&gt; 不是std::vector&lt;T, A&gt;

    【讨论】:

      【解决方案3】:

      如果每个模板只有一个参数,你可以采取一种捷径:

      template <template<class> class... Templates, class... Types>
      template_pack(const Templates<Types>&...) -> template_pack<Templates...>;
      

      每个参数只有一个参数,很容易在所有模板中拆分一个包。

      不幸的是,我不知道有什么方法可以在不知道模板数量的情况下为每个模板单独打包。因此,似乎需要通过助手进行间接层。此外,演绎指南必须采用-&gt; template_pack&lt;something&gt; 的形式,大概是为了避免让编译器做太多工作或遇到不可能的问题。鉴于此,该类需要稍作调整:

      template <template <class...> class... Templates>
      class holder {};
      
      // Class definition
      template<class Holder>
      class template_pack;
      
      template <template <class...> class... Templates>
      class template_pack<holder<Templates...>>
      {
          public:
          template <class... Types>
          constexpr template_pack(const Types&...) noexcept {}
      };
      

      通过这个调整,我们可以创建一个助手(可能可以简化为更直接):

      template<template<class...> class... TTs>
      struct result {
          using type = holder<TTs...>;
      };
      
      template<class T>
      struct type {};
      
      template<class Prev, class Current, class... Rest>
      auto helper() {
          return []<template<class...> class... PrevTTs, template<class...> class CurrTT, class... CurrTs>(result<PrevTTs...>, type<CurrTT<CurrTs...>>) {
              if constexpr (sizeof...(Rest) == 0) {
                  return result<PrevTTs..., CurrTT>{};
              } else {
                  return helper<result<PrevTTs..., CurrTT>, Rest...>();
              }
          }(Prev{}, type<Current>{});
      }
      

      我使用 C++20 的模板化 lambda 将两个模板从它们的 arg 包内联中分离出来,而不是有一个额外的帮助层,但是在早期的标准中仍然可以使用这个额外的层,只是更丑。帮助器递归地获取先前的结果,一次拉开一个模板,将其添加到结果中,然后递归地调用自身,直到没有剩下的参数为止。

      有了这个帮手,就可以制作推演指南了:

      // Class template argument deduction guide
      template <typename... Ts>
      template_pack(const Ts&...) -> template_pack<typename decltype(helper<result<>, Ts...>())::type>;
      

      您可以找到full example here。也可以对这段代码进行一些显着的改进,但核心思想就在那里。

      【讨论】:

        【解决方案4】:

        类似的东西似乎在起作用

        #include <iostream>
        #include <vector>
        #include <list>
        #include <deque>
        
        template<typename... TS>
        struct Pack;
        
        template<typename S, typename... TS>
        struct Pack<S, TS...> {
            S s;
            Pack<TS...> ts;
            static constexpr size_t size = Pack<TS...>::size + 1;
        
            constexpr Pack(S&& s, TS&&... ts) noexcept
                : s(s)
                , ts(std::forward<TS>(ts)...)
            {}
        };
        
        template<typename S>
        struct Pack<S> {
            S s;
            static constexpr size_t size = 1;
        
            constexpr Pack(S&& s) noexcept
                : s(s)
            {}
        };
        
        template<>
        struct Pack<> {
            static constexpr size_t size = 0;
        };
        
        template<typename... TS>
        constexpr auto make_pack(TS&&... ts) noexcept {
            return Pack<TS...>(std::forward<TS>(ts)...);
        }
        
        int main() {
            auto empty_pack = make_pack();
            std::cout << empty_pack.size << std::endl;  // 0
        
            auto vector_pack = make_pack(std::vector<int>{});
            std::cout << vector_pack.size << std::endl;  // 1
        
            auto vector_list_deque_pack = make_pack(std::vector<int>{}, std::list<double>{}, std::deque<char>{});
            std::cout << vector_list_deque_pack.size << std::endl;  // 3
        }
        

        【讨论】:

          猜你喜欢
          • 2021-06-20
          • 2020-05-09
          • 1970-01-01
          • 1970-01-01
          • 2021-11-19
          • 1970-01-01
          • 2018-02-16
          • 1970-01-01
          • 2016-07-20
          相关资源
          最近更新 更多