C ++: возможно ли написать функцию, которая добавляет элемент с другим типом к массиву вариантов? - PullRequest
0 голосов
/ 20 сентября 2018

Я пытаюсь написать функцию, которая, например, примет:

std::array<std::variant<int, std::string_view>, 4>

и double d и вернет:

std::array<std::variant<int, std::string_view, double>, 5>

с добавлением d в конец массива, но я до сих пор сталкиваюсь с очень странным поведением в моей реализации.

Прямо сейчас у меня естьVariant_Monoid (с бинарной операцией concatenate) для обработки расширяющихся типов std::variant, что, кажется, работает нормально:

struct Variant_Monoid {
    using zero = std::monostate;

    template <typename T, typename... Args>
    struct [[maybe_unused]] concatenate;

    template <typename... Args0, typename... Args1>
    struct concatenate<std::variant<Args0...>, std::variant<Args1...>> {
        using type = std::variant<Args0..., Args1...>;
    };

    // Convenience method to concatenate types without having to wrap them into a variant first.
    template<typename... Args0, typename... Args1>
    struct concatenate<std::variant<Args0...>, Args1...> {
        using type = std::variant<Args0..., Args1...>;
    };
};

Тогда у меня есть идея, где идея состоит в том, чтобы просмотреть существующий массивпопробуйте расширить типы из старого варианта в новый, а затем добавьте новый элемент:

template<size_t N, typename T, typename S>
constexpr std::array<typename Variant_Monoid::concatenate<T, S>::type, N + 1> append_element_with_type(const std::array<T, N> &a, const S &s) {
    return append_element_with_type_aux(a, s, std::make_index_sequence<N>{});
}

, делегирующий этой функции:

template<size_t N, typename T, typename S, size_t... Indices>
constexpr std::array<typename Variant_Monoid::concatenate<T, S>::type, N + 1>
append_element_with_type_aux(const std::array<T, N> &a,
                             const S &s, std::index_sequence<Indices...>) noexcept {
    return {{std::visit([](auto &&t) { return t; }, a[Indices])..., s}};
}

Поведение этой функцииОднако повсюду.В некоторых случаях (обычно это первый звонок в серии звонков) он работает нормально.Например:

constexpr std::array<std::variant<Variant_Monoid::zero>, 0> s0{};
constexpr std::array<std::variant<Variant_Monoid::zero, int>, 1> s1 = append_element_with_type(s0, 1);

Однако, если я попытаюсь сделать это дальше, компилятор не только пожалуется, что выражение не constexpr, но и не сможет полностью скомпилироваться с кучей неразборчивых сообщений STL:

auto s2 = append_element_with_type(s1, 3.14159);

Аналогично, это, кажется, работает без проблем:

constexpr std::array<std::variant<std::string_view>, 3> to_extend = {{"Hello", "there", "world"}};
constexpr std::array<std::variant<std::string_view, int>, 4> extended = append_element_with_type(to_extend, 5);

, а затем снова происходит сбой с аналогичными ошибками компиляции и снова с утверждением, что выражение не constexpr:

constexpr std::array<std::variant<std::string_view, int, double>, 5> prepended = append_element_with_type(extended, 3.14);

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

1 Ответ

0 голосов
/ 20 сентября 2018

Вызов посетителю должен привести к тому же типу и категории значения для всех возможных аргументов.Явным образом укажите тип возвращаемого значения лямбды в качестве нового варианта, то есть typename Variant_Monoid::concatenate<T, S>::type.

...