Предположим, у меня есть кортеж, который по сути является оберткой вокруг std::tuple
(поэтому решение для std::tuple
тоже работает). Как бы я написал эквивалент конструктора/tuple_cat, который может принимать произвольное количество аргументов одного и того же шаблонного типа, но каждый из которых имеет подмножество шаблонных аргументов возвращаемого значения.
Проблема с использованием std::tuple_cat
заключается в том, что тип возвращаемого значения основан на аргументах. Я хотел бы иметь фиксированный тип возвращаемого значения, чтобы любой член, не переданный внутри кортежа, создавался по умолчанию. Это также позволяет дублировать типы, хотя я хотел бы допустить ошибку или любое совпадение аргументов типа с аргументами. Заказ тоже сохраняется
Пример того, что я имею в виду: ограничения в комментариях накладываются с помощью предложений require.
template<typename... Ts>
// Ts constrained to only one of each type
struct MyTuple {
template<typename... Tuples>
// Tuples constrained to all be instantiations of MyTuple
// each Tuples<Us...> constrained that Us is strictly a subset of types in Ts
// every pair of Tuples<Us...> constrained to not have any overlap in their Us
// const& used for simplicity to not get into std::forward-ing everything
MyTuple(Tuples const&... args)
{
// no clue
// using initializer list would be better, but even less of a clue on that
}
std::tuple<Ts...> mTuple;
}
Использование будет примерно таким:
// A..E are default constructible, distinct types
using SuperTuple = MyTuple<A, B, C, D, E>;
// A, B, C and E are passed in as arguments, D is default-constructed
auto super = SuperTuple{MyTuple<A>{}, MyTuple<E,B>{}, MyTuple<C>{}};
Возможно ли это вообще?
Для каждого кортежа и каждого типа вызовите get<T>(mTuple) = get<T>(otherTuple)
.
@Алан Откуда ты узнал, что это домашнее задание?
@Алан Это не копипаста. Я намеренно не добавил пункты «требования», потому что они без необходимости расширяли бы вопрос. Важно то, что они присутствуют и что они делают. Поэтому любой ответ не должен касаться, например. обработка повторяющихся типов. Я попробовал tuple_cat и описал, почему он не подходит. Я попробовал предложение @Osyotr: сначала развернуть Ts
, затем Tuples
, затем проверить if constexpr
, имеет ли j-й кортеж i-й тип, а затем std::get
извлечь его из него. Это запускает ctor по умолчанию для всех Ts
, а затем назначает их, а не только при необходимости.
«Они напрасно продлили бы вопрос...» Нет, не будут. Вместо этого они покажут, что вы попытались решить свой вопрос.
Я предполагаю, что при написании ограничений у вас есть черта или концепция tuple_contains<T, Tuple>
, которая проверяет, есть ли в кортеже Tuple
элемент типа T
.
Что вам нужно сделать, так это построить декартово произведение ваших пакетов параметров. Это можно сделать с помощью вспомогательной функции для каждого уровня расширения.
template <typename TupleIn, typename TupleOut, typename T>
void assign_impl_inner(const TupleIn& in, TupleOut& out) {
if constexpr(tuple_contains<T, TupleIn>) {
get<T>(out) = get<T>(in);
}
}
template <typename TupleIn, typename TupleOut, typename... Ts>
void assign_impl(const TupleIn& in, TupleOut& out) {
(assign_impl_inner<TupleIn, TupleOut, Ts>(in, out), ...);
}
template<typename... Ts>
// Ts constrained to only one of each type
struct MyTuple {
template<typename... Tuples>
// Tuples constrained to all be instantiations of MyTuple
// each Tuples<Us...> constrained that Us is strictly a subset of types in Ts
// every pair of Tuples<Us...> constrained to not have any overlap in their Us
// const& used for simplicity to not get into std::forward-ing everything
MyTuple(Tuples const&... args)
{
(assign_impl<Tuples, std::tuple<Ts...>, Ts...>(args, mTuple), ...);
}
std::tuple<Ts...> mTuple;
};
Вот реализация, которая
Сложность ваших требований заключается в том, что конструктор std::tuple
должен иметь правильную арность. Чтобы исправить это, мы создаем тип «идентичности» default_construct_t
, который поступает правильно, когда нам не хватает аргумента, аналогично тому, как 0
является тождеством для сумм.
Чтобы избежать рекурсии или большого объема метапрограммирования при попытке индексации пакета кортежей, мы удобно повторно используем default_construct_t
для свертывания типов в std::common_type_t
.
#include<tuple>
#include<utility>
struct default_construct_t
{
using type = default_construct_t;
template<typename T>
operator T() { return T(); }
};
template<typename, typename>
struct has_element : std::false_type
{
};
template<typename T, typename... Args>
requires ((std::is_same_v<T, Args> || ...))
struct has_element<T, std::tuple<Args...>>
: std::true_type
{
};
template<typename T, typename Tup>
concept contains = has_element<T, std::decay_t<Tup>>::value;
template<typename T, typename... Tuples>
decltype(auto) find(Tuples&&... tuples)
{
using R = std::common_type_t<
std::conditional_t<
contains<T, decltype(tuples)>,
std::type_identity<decltype(tuples)>,
default_construct_t
>...
>::type;
if constexpr(std::is_same_v<R, default_construct_t>)
return default_construct_t{};
else
return std::get<T>(std::get<R>(
std::forward_as_tuple(std::forward<Tuples>(tuples)...)
));
}
template<typename... Ts>
struct MyTuple
{
template<typename... Tuples>
MyTuple(Tuples&&... tuples)
: mTuple{find<Ts>(std::forward<Tuples>(tuples)...)...}
{
}
std::tuple<Ts...> mTuple;
};
В прямом эфире .
Это ничем не отличается от копирования вопроса домашнего задания. Что вы пробовали до сих пор? Пробовали ли вы добавить хотя бы некоторые из требований.