转置结构容器

时间:2019-09-19 12:12:03

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

让我们有一个struct Record{uint8_t x, y;};,一个结构容器Container<Record>和一个struct Transposed{Container<uint8_t> x,y};。容器c是一个模板,其第一个arg是值的类型,其余所有args均具有默认值。例如,它可以是std::vector(其余arg是类型)或std::span(其余arg是值)。该模板应适用于所有这些模板。另外,我们可能希望将其余模板参数传递给基础模板。

我们如何使用模板从容器中获取Transposed

我尝试了可变参数模板,

#include <iostream>
#include <vector>

template <typename value_type=uint8_t> struct Record{
    value_type x, y;
};

template<typename value_type, typename value_type2=uint8_t> class ContainerA: public std::vector<value_type>{
    value_type2 b=1u;
};

template<typename value_type, uint8_t int_value=1u> class ContainerB: public std::vector<value_type>{};

template<typename value_type, template <typename ...> typename container_type> class Transposed{
    container_type<value_type> x, y;

    public:
        Transposed(container_type<Record<value_type>> & recs){
            x.reserve(recs.size());
            y.reserve(recs.size());
            x.resize(recs.size());
            y.resize(recs.size());
            size_t i=0;
            for(auto &rec :recs){
                x[i] = rec.x;
                y[i] = rec.y;
                ++i;
            }
        }
};

int main(){
    std::vector<Record<uint8_t>> recsV{
        {1, 2},
        {3, 4}
    };
    Transposed trV{recsV};
    std::cout<<"vec"<<std::endl;

    ContainerA<Record<uint8_t>> recsA{
        {1, 2},
        {3, 4}
    };
    Transposed trA{recsA};
    std::cout<<"A"<<std::endl;

    /*ContainerB<Record<uint8_t>> recsB{
        {1, 2},
        {3, 4}
    };
    Transposed trB{recsB};
    std::cout<<"B"<<std::endl;*/
    return 0;
}

但是似乎它们不能同时匹配类型和值。不允许使用超过1个可变参数模板参数。这是C ++语言的缺陷还是在设计上的故意选择,是否需要使用any_template_arg之类的关键字,还是应该只允许指定2个不同类型的可变参数才能使用此用例?

2 个答案:

答案 0 :(得分:1)

据我所知,还没有一种将类型和值(以及模板模板)模板参数匹配在一起的方法。我已经搜索了很长时间。

因此,我没有找到一种简单而优雅的方式来制作想要的东西的方法。

尝试回答您的问题

  

我们如何使用模板从容器中获取Transposed

我能想象的最好的方法是声明(不一定要定义它们仅在decltype()内部使用)几个简单的函数,如下所示:

template <typename VT,
          template <typename...> typename CT>
CT<VT> extract_func (CT<Record<VT>>);

template <typename VT,
          template <typename, auto...> typename CT>
CT<VT> extract_func (CT<Record<VT>>);

第一个是当Record容器接受可变的类型列表(CTstd::vector情况下,删除ContainerA部分;第二个用于CT容器(在type参数之后)接受一个或多个值(ContainerB情况)。

很显然,这并不涵盖所有可能的情况,但是声明其他extract_func()函数来涵盖其他情况却微不足道。

现在您可以如下声明Tranposed

template <typename T,
          typename CT = decltype(extract_func(std::declval<T>()))>
class Transposed

请注意,Transposed现在接受通用类型T,但是只有在T类型与extract_func()声明匹配(作为参数)时才启用SFINAE。

Transposed主体中,您可以使用CT声明x以及yT作为构造函数的参数。

以下是完整的编译示例

#include <iostream>
#include <vector>

template <typename value_type=std::uint8_t>
struct Record
 { value_type x, y; };

template <typename value_type, typename value_type2=std::uint8_t>
class ContainerA : public std::vector<value_type>
 { value_type2 b=1u; };

template <typename value_type, std::uint8_t int_value=1u>
class ContainerB : public std::vector<value_type>
 { };

template <typename VT,
          template <typename...> typename CT>
CT<VT> extract_func (CT<Record<VT>>);

template <typename VT,
          template <typename, auto...> typename CT>
CT<VT> extract_func (CT<Record<VT>>);

template <typename T,
          typename CT = decltype(extract_func(std::declval<T>()))>
class Transposed
 {
   private:
      CT x, y;

   public:
      Transposed (T & recs)
       {
         x.reserve(recs.size());
         y.reserve(recs.size());
         x.resize(recs.size());
         y.resize(recs.size());
         std::size_t i=0u;
         for(auto &rec :recs){
            x[i] = rec.x;
            y[i] = rec.y;
            ++i;
         }
       }
 };

int main ()
 {
   std::vector<Record<std::uint8_t>> recsV { {1, 2}, {3, 4} };

   Transposed trV{recsV};

   std::cout<<"vec"<<std::endl;

   ContainerA<Record<std::uint8_t>> recsA { };

   Transposed trA{recsA};

   std::cout<<"A"<<std::endl;

   ContainerB<Record<std::uint8_t>> recsB { };

   Transposed trB{recsB};

   std::cout<<"B"<<std::endl;
 }

答案 1 :(得分:0)

一个适合您的示例:

template<typename value_type=uint8_t>
struct Record{
    value_type x, y;
};

template<class T>
std::vector<T> rebind_container(std::vector<Record<T>> const&);

// Span transposes into vector.
template<class T>
std::vector<T> rebind_container(std::span<Record<T>> const&); 

template<class T>
std::list<T> rebind_container(std::list<Record<T>> const&);

inline void reserve(...) {}

template<class... Args>
inline void reserve(std::vector<Args...>* v, size_t n) {
    v->reserve(n);
}

template<class container_type>
struct Transposed {
    using container_type2 = decltype(rebind_container(std::declval<container_type>()));

    container_type2 x, y;

    Transposed(container_type const& recs) {
        auto const n = recs.size();
        reserve(&x, n);
        reserve(&y, n);
        for(auto& rec : recs) {
            x.push_back(rec.x);
            y.push_back(rec.y);
        }
    }
};

int main(){
    std::vector<Record<uint8_t>> recsV{
        {1, 2},
        {3, 4}
    };
    Transposed<decltype(recsV)> trV{recsV};
    std::cout << trV.x.size() << std::endl;

    std::list<Record<uint8_t>> recsV2{
        {1, 2},
        {3, 4}
    };
    Transposed<decltype(recsV2)> trV2{recsV2};
    std::cout << trV2.x.size() << std::endl;
}