【问题标题】:Unpacking a typelist解压类型列表
【发布时间】:2017-02-02 12:19:33
【问题描述】:

假设我有一个只接受类型模板参数的函数,我无法更改它的定义/实现。

template < typename T >
void do_it();

现在我有一个以通常方式定义的类型列表,也无法更改:

template< typename ...Ts >
struct typelist;

我想实现一个函数,它接受一个类型列表,并在每个类型上运行 do_it():

template< typename List >
void do_them();

到目前为止,我发现的唯一解决方案是:

template< typename T >
void do_them_impl()
{
   do_it<T>();
}

template< typename T, typename Ts...>
void do_them_impl()
{
   do_it<T>();
   do_them_impl<Ts...>();
}

template< template < typename...> class List, typename ...Ts >
void do_them_extract( List<Ts...>&& )
{
    do_them_impl< Ts >(); 
}

template< typename List >
void do_them()
{
    do_them_impl( List{} ); 
}

但这需要 4(!) 个函数,我想创建一个 do_them 函数。我将需要其中的很多,而且我不想为每个函数编写一个四边形。我错过了什么吗?

欢迎使用 C++14,也欢迎 C++17 解决方案,但标记为这样。

【问题讨论】:

    标签: c++ templates c++14 variadic-templates c++17


    【解决方案1】:

    在 C++14 中,您可以使用一些可怕的技巧来引入有效的包扩展上下文:

    template< template < typename...> class List, typename ...Ts >
    void do_them_impl( List<Ts...>&& )
    {
        (void)std::initializer_list<int> {
            (do_it<Ts>(), 0)...  
        };
    }
    
    template< typename List >
    void do_them()
    {
        do_them_impl( List{} ); 
    }
    

    这使您可以避免递归模板实例化,这通常更昂贵。

    Live Demo


    在 C++17 中你可以使用fold expressions:

    template< template < typename...> class List, typename ...Ts >
    void do_them_impl( List<Ts...>&& )
    {       
        (do_it<Ts>(), ...);
    }
    
    template< typename List >
    void do_them()
    {
        do_them_impl( List{} ); 
    }
    

    Live Demo

    【讨论】:

    • 太快了! C++14 - 是否有可能避免 std::initializer_list? (我在没有 STL 的情况下编码,各种原因)。 C++17 版本 - 天哪,我等不及 Visual Studio 支持它了 -_-
    • @KornelKisielewicz 是的,您可以使用整数数组或类似的东西:int _[] = { 0, (do_it&lt;Ts&gt;(), 0)... };。第一个 0 用于避免 0 大小的数组,这在 C++ 中无效。
    • @KornelKisielewicz 我不会为此添加另一个答案。值得更新这个并添加带有数组的示例。
    • 我会 24 小时开放这个问题,如果没有任何巧妙的弹出,我会接受这个 - 为我节省了 2 个函数(我可以忍受将 void do_them() 更改为 void do_them ( typelist&& ) )
    • @KornelKisielewicz 类似于 this 你在找什么?
    【解决方案2】:

    这是一个利用 C++14 的通用 lambda 的解决方案:

    template <typename T>
    struct type_ { using type = T; };
    
    template <typename Type>
    using type = typename Type::type;
    
    template <typename List>
    struct map_;
    
    template <template <typename...> typename Container, typename... Ts>
    struct map_<Container<Ts...>>
    {
        template <typename Fun>
            void operator()(Fun&& fun)
            {
                (void)((int[sizeof...(Ts)]){(fun(type_<Ts>{}), 0)...});
            }
    };
    
    template <typename List>
    auto map = map_<List>{};
    

    然后对于每个函数

    #include <iostream>
    #include <cxxabi.h>
    #include <typeinfo>
    
    template <typename T>
    const char * type_name()
    {
            return abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, nullptr);
    }
    
    template <typename T>
    void do_it()
    {
        std::cout << type_name<T>() << std::endl;
    }
    

    你可以写:

    template <typename List>
    void do_them()
    {
        map<List>([](auto v){ do_it<type<decltype(v)>>(); });
    }
    
    template <typename... Ts>
    struct typelist {};
    
    int main()
    {
        do_them<typelist<int, char, bool>>();
        return 0;
    }
    

    使用 -O3 编译会得到与我们简单地连续调用 do_it&lt;int&gt;、do_it&lt;char&gt;、do_it&lt;bool&gt; 相同的程序集。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-12-06
      • 2013-06-15
      • 2013-10-22
      • 2015-10-16
      • 1970-01-01
      • 2021-12-22
      • 2016-12-26
      相关资源
      最近更新 更多