Как построить кортеж с надмножеством типов из кортежей с непересекающимся подмножеством типов

Предположим, у меня есть кортеж, который по сути является оберткой вокруг 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>{}};

Возможно ли это вообще?

Это ничем не отличается от копирования вопроса домашнего задания. Что вы пробовали до сих пор? Пробовали ли вы добавить хотя бы некоторые из требований.

Alan 22.08.2024 10:44

Для каждого кортежа и каждого типа вызовите get<T>(mTuple) = get<T>(otherTuple).

Osyotr 22.08.2024 11:00

@Алан Откуда ты узнал, что это домашнее задание?

Passer By 22.08.2024 11:11

@Алан Это не копипаста. Я намеренно не добавил пункты «требования», потому что они без необходимости расширяли бы вопрос. Важно то, что они присутствуют и что они делают. Поэтому любой ответ не должен касаться, например. обработка повторяющихся типов. Я попробовал tuple_cat и описал, почему он не подходит. Я попробовал предложение @Osyotr: сначала развернуть Ts, затем Tuples, затем проверить if constexpr, имеет ли j-й кортеж i-й тип, а затем std::get извлечь его из него. Это запускает ctor по умолчанию для всех Ts, а затем назначает их, а не только при необходимости.

Demiu 22.08.2024 11:15

«Они напрасно продлили бы вопрос...» Нет, не будут. Вместо этого они покажут, что вы попытались решить свой вопрос.

user12002570 22.08.2024 11:40
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
5
81
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я предполагаю, что при написании ограничений у вас есть черта или концепция 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;
};

Смотрите на колиру

Ответ принят как подходящий

Вот реализация, которая

  1. По умолчанию не создает ненужные элементы
  2. Направляется правильно

Сложность ваших требований заключается в том, что конструктор 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;
};

В прямом эфире .

Другие вопросы по теме