Как определить вектор векторов разных типов, используя шаблон с переменными координатами? - PullRequest
0 голосов
/ 08 октября 2018

Я бы хотел сделать вектор векторов разных типов.Предположим, у меня есть 4 класса;Собака, Кот, Свинья, Корова.Мне бы хотелось, чтобы вектор содержал векторы каждого из них, и чтобы я мог получить к ним доступ по двум индексам, чтобы я мог выполнять итерации по ним, что было бы в случае, если бы это был вектор векторов.

У меня естьиграл с такими вещами, как:

std::vector<std::variant<std::vector<Dog>,
    std::vector<Cat>,
    std::vector<Pig>,
    std::vector<Cow>>>;

Кроме того, я хотел бы иметь возможность создавать эти массивы с помощью вариабельной шаблонной конструкции, чтобы я мог легко создать еще один вектор векторов, скажем, Apple, Pear, Orange, Lemon, Grape, Cherry.

Я хотел бы иметь возможность написать в своем коде что-то вроде:

MyVectorOfVectors<Dog,Cat,Pig,Cow> animals;
MyVectorOfVectors<Apple, Pear, Orange, Lemon, Grape, Cherry> fruits;

и иметь такие, которые создают векторы для каждого типа и хранят эти векторыв другом векторе (или аналогичном), к которому я могу получить доступ по индексу.Очевидно, этот вектор должен быть своего рода гетерогенным контейнером, как предложено выше, с вектором вариантов.Я полагаю, что это должно быть заключено в какое-то определение класса вариационного шаблона.

Так что для доступа к третьему Dog в массиве потребуется функциональность, подобная этой:

Dog mydog = animals[0][3];

илиесли решение нужно было обернуть в класс,

Dog mydog = animals.thearray[0][3];

Я понимаю, что это можно реализовать с помощью иерархии классов, динамического размещения и указателей на объекты, но я ищу решение с плоскимМодель памяти для исполнения.

Ответы [ 4 ]

0 голосов
/ 27 октября 2018

Спасибо за предложения.Я использовал ответ Макса Ланхофа для создания структуры данных и выяснил способ выполнения итерации.

Вот код, с которым я закончил.

#include <iostream>
#include <tuple>
#include <vector>
#include <cstddef>
#include <limits>

template<typename... Ts>
using TupleVector = std::tuple<std::vector<Ts>...>;

constexpr std::size_t size_t_max = std::numeric_limits<std::size_t>::max();

template<typename T, std::size_t N = size_t_max, typename FuncT>
void for_all(T b, FuncT F)
{
  constexpr std::size_t TupSize = std::tuple_size<T>::value;

  // Entry point: No 'N' given - default value.
  if constexpr (N == size_t_max) 
  {
    for_all<T,TupSize-1>(b,F); // So call again with tuple size - 1
  }
  else
  {
    // Actually do it. Loop through vector and call lambda, 
    for (auto &i : std::get<TupSize-N-1>(b))
    {
      F(i);
    }
    // If N == 0 escape, otherwise recurse.
    if constexpr (N > 0) 
    {
      for_all<T,N-1>(b,F); // Recursively call with N - 1
    }
  }
}

struct Dog { int age;  };
struct Cat { int age;  };
struct Pig { int age;  };
struct Cow { int age;  };

int main()
{
  using farmvec = TupleVector<Dog, Cat, Pig, Cow>;
  farmvec animals;
  std::get<0>(animals).push_back(Dog());
  std::get<0>(animals)[0].age=1;
  std::get<0>(animals).push_back(Dog());
  std::get<0>(animals)[1].age=5;

  std::get<1>(animals).push_back(Cat());
  std::get<1>(animals)[0].age=2;

  std::get<2>(animals).push_back(Pig());
  std::get<2>(animals)[0].age=3;

  std::get<3>(animals).push_back(Cow());
  std::get<3>(animals)[0].age=4;

  for_all<farmvec>(animals,[](auto a)
            {
              std::cout << "Age: " << a.age << std::endl;
            });
  return 0;
}

Как вы можете видеть, он работает с неиерархическими классами с использованием вывода типов, поэтому нет необходимости в моих Dog, Cat и т. Д.Производный от базового класса.

Следующий шаг - возможность использовать параметры с лямбда-выражением, но, по крайней мере, я дошел до этого.

0 голосов
/ 08 октября 2018

Я не уверен, что это то, что вам нужно, для меня это странное требование:

template<typename ...Ts>
using MatrixVariantRows = std::vector<std::variant<std::vector<Ts>...>>;

https://wandbox.org/permlink/Jl9j29TgyaXKZAvh

0 голосов
/ 21 октября 2018

В зависимости от ваших требований, здесь есть еще одна возможность.Компромисс состоит в том, что с точки зрения компиляторов вы можете смешивать животных или фрукты (но не животных и фрукты), но вариант гарантирует, что во время выполнения вы не сможете получить доступ к неправильному типу.

struct Dog {};
struct Cat {};
struct Pig {};
struct Cow {};
struct Apple {};
struct Pear {};
struct Orange {};
struct Lemon {};
struct Grape {};
struct Cherry {};

template<typename... Ts>
using MetaVector = std::vector<std::vector<std::variant<Ts...>>>;

int main(int argc, const char *argv[])
{
    Dog daisy, sadie, molly;
    Cat sam, coco, tiger;
    Pig frankie, albert, digger;

    MetaVector<Dog,Cat,Pig,Cow> animals = {{ daisy, sadie, molly },
                                           { sam, coco, tiger },
                                           { frankie, albert, digger }};
    auto dog0 = std::get<Dog>(animals[0][0]);
    auto dog1 = std::get<Dog>(animals[0][1]);
    auto dog2 = std::get<Dog>(animals[0][2]);

    auto cat0 = std::get<Cat>(animals[0][0]); // throws exception

    MetaVector<Apple,Pear,Orange,Lemon,Grape,Cherry> fruits;

    return 0;
}
0 голосов
/ 08 октября 2018
template<typename... T>
using MyVectorOfVectors = std::tuple<std::vector<T>...>;

MyVectorOfVectors<Dog,Cat,Pig,Cow> animals;
MyVectorOfVectors<Apple, Pear, Orange, Lemon, Grape, Cherry> fruits;

void foo()
{
    std::vector<Dog>& dogs = std::get<0>(animals);
    std::vector<Orange>& oranges = std::get<2>(fruits);
}

Демо

Вы должны решить: либо вы можете выводить типы во время компиляции (в этом случае ваши индексы в MyVectorOfVectors также должны быть известны во время компиляции) - тогда вы получите все типы безопасности (как указано выше).

Если ваши индексы также могут быть значениями во время выполнения, то вам нужна форма стирания типа.Это будет связано с накладными расходами времени выполнения, которые, как вы сказали, вы хотели бы избежать.

В любом случае вы не получите Dog dog = animals[0][3], потому что параметр operator[] для любого MyVectorOfVectors в конечном итоге будетне обязательно известен во время компиляции (по крайней мере, с точки зрения компилятора).

...