У меня есть шаблонная структура ContainerInner
, содержащая кортеж с векторами типов из вариативного шаблона, и структура Container
, содержащая кортеж ContainerInner
, указанный в ее шаблоне. Код выглядит следующим образом:
template<typename... Args>
struct ContainerInner
{
std::tuple<std::vector<Args>...> m_elements;
template<typename T>
static constexpr bool containsOne()
{
return (std::is_same_v<T, Args> || ...);
}
template<typename... T>
static constexpr bool contains()
{
return (containsOne<T>() && ...);
}
template<typename T>
std::vector<T> &get()
{
return std::get<std::vector<T>>(m_elements);
}
};
template<typename... T>
struct ArgList {
template<typename... TTS>
using add = ArgList<T..., ContainerInner<TTS...>>;
};
template<typename ListArgs>
struct Container;
template<typename... Args>
struct Container<ArgList<Args...>>
{
std::tuple<Args...> m_containers;
// It also crashes with std::tuple<Args&...> return type
template<typename... T>
inline auto getTuples()
{
auto tpl = std::make_tuple<Args&...>(std::get<Args>(m_containers)...);
return tpl;
}
template<typename... T>
ContainerInner<T...> &get()
{
return std::get<ContainerInner<T...>>(m_containers);
}
};
using MyArgList = ArgList<>
::add<char, int>
::add<char, float>
::add<char, int, float>;
int main(int argc, char* args[])
{
Container <MyArgList> cnt;
auto arr = cnt.getTuples<float>();
std::get<ContainedType<char, int>>(arr).get<int>().push_back(3);
std::cout << typeid(arr).name() << std::endl;
std::cout << cnt.get<char, int>().get<int>().size() << std::endl;
}
Я хочу изменить метод getTuples
так, чтобы он возвращал кортеж со ссылками на все ContainerInner
из m_containers
, содержащие указанные типы, что является условием, обрабатываемым статическим методом contains
. Я подумал, что могу использовать такую концепцию:
template<typename InnerCnt, typename T>
concept ContainedType =
requires(T a, InnerCnt b) {
b.contains<T>();
};
чтобы рекурсивно перебрать шаблон с переменным числом вариантов и использовать std::tuple_cat
для построения окончательного массива, но не понял, как это сделать. Как я могу решить эту проблему?
РЕДАКТИРОВАТЬ. Я решил это, но, вероятно, не оптимальным способом. Я добавил обертку Query
поверх перегруженного кортежа operator+
и использовал ее для разворачивания:
template<typename... Args>
struct Query
{
std::tuple<Args...> m_tpl;
template<typename... TT>
constexpr inline auto operator+(const Query<TT...> &rhs_)
{
return Query<Args..., TT...>(std::tuple_cat(m_tpl, rhs_.m_tpl));
}
template<typename... TT>
constexpr inline auto &get()
{
return std::get<ContainerInner<TT...>&>(m_tpl);
}
};
template<typename LST, typename CONTAINER_INNER>
constexpr inline auto getQueryElem(CONTAINER_INNER &t_)
{
return Query<>();
}
template<typename LST, typename CONTAINER_INNER> requires Contained<CONTAINER_INNER, LST>
constexpr inline auto getQueryElem(CONTAINER_INNER &t_)
{
return Query(std::tuple<CONTAINER_INNER&>(t_));
}
// Inside Container
template<typename... T>
constexpr inline auto getTuples()
{
return (getQueryElem<Typelist<T...>>(std::get<Args>(m_containers)) + ...);
}
int main(int argc, char* args[]) {
Container <MyArgList> cnt;
auto arr = cnt.getTuples<int>();
std::cout << typeid(arr).name() << std::endl;
cnt.get<char, int>().get<int>().push_back(5);
arr.get<char, int>().get<int>().push_back(-99);
std::cout << cnt.get<char, int>().get<int>() << std::endl;
std::cout << arr.get<char, int>().get<int>() << std::endl;
}
Кажется, это работает, но я не уверен, насколько хорошо компилятор это оптимизирует, на самом деле он может создавать временные запросы во время развертывания во время выполнения.
@Jarod42 Jarod42 Я уже это исправил, оно скомпилировалось в msvc, но не в gcc или clang.
Можете ли вы предоставить работоспособный код (возможно, со ссылкой)? Так что мы можем экспериментировать. Мне непонятно, чего вы ожидаете от возвращения getTuples
...
@Jarod42 Конечно, вот ссылка . Этот код обращается к вектору напрямую и через возвращенную ссылку и проверяет, что это тот же вектор, а не копия. Он возвращает то, что ожидается, за вычетом форматирования имени типа gcc.
Проблемы от getTuples()
:
std::make_tuple
предназначен для вывода его параметра, который вы хотите std::tuple
напрямую:auto getTuples()
{
auto tpl = std::tuple<Args&...>(std::get<Args>(m_containers)...);
return tpl;
}
или используйте std::tie
:
auto getTuples()
{
return std::tie(std::get<Args>(m_containers)...);
}
Судя по вашему редактированию, вы хотите отфильтровать (во время компиляции) некоторые элементы кортежа, поэтому может помочь использование std::tuple_cat
с функцией, которая возвращает пустой tuple
или tuple
с элементом:
template <typename... Ts, typename... Us>
auto filter(ContainerInner<Us...>& c)
{
if constexpr (ContainerInner<Us...>::template contains<Ts...>()) {
return std::tie(c);
} else {
return std::tuple<>{};
}
}
А потом
template <typename... Ts>
constexpr auto getTuples()
{
return std::tuple_cat(filter<Ts...>(std::get<Args>(m_containers))...);
})
Посмотрите на getTuples
после редактирования, он использует свой параметр, я просто не знал, как его использовать в данный момент.
Отредактировано, чтобы добавить ваш фильтр.
Большое спасибо, я не знал, что вы можете сделать это с помощью 1 функции с помощью if constexpr
здесь
Альтернативой может быть перегрузка (с SFINAE или диспетчеризацией тегов).
b.template contains<T>();
?