Std :: tuple получить все, кроме последнего элемента

Я пытаюсь вернуть все, кроме последнего элемента в std::tuple, если в кортеже всего два элемента, вернуть первый. Поскольку std::tuple имеет много средств времени компиляции, тип с двойным возвратом должен быть выполнимым. Вот что у меня есть на данный момент:

// Behavior
// init(std::make_tuple(1,2)) = 1
// init(std::make_tuple(1,2,3)) = (1,2)

// First case
template<class T1, class T2>
inline static T1 init(Tuple<T1, T2> t) {
    return std::get<0>(t);
}

// Second case
template<class ...Args, class S = std::make_index_sequence<sizeof...(Args) - 1>>
inline static decltype(auto) init(Tuple<Args...> t) {
    return std::apply([](const auto &item...) {
        return std::tuple_cat(std::make_tuple(std::get<S>) ... std::tuple<>);
    }, t);
}

Было бы здорово, если бы я мог сделать это удобным для C++ 17 способом. Я получаю следующую ошибку с описанной выше реализацией:

./tuple.cpp:36:55: error: pack expansion does not contain any unexpanded parameter packs
return std::tuple_cat(std::make_tuple(std::get) ... std::tuple<>);

~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^
1 error generated.

вы хотите вернуть кортеж со всеми элементами, кроме последнего?

user7860670 12.08.2018 18:34
std :: ignore может вам помочь, я думаю, возможно.
Jesper Juhl 12.08.2018 19:21
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
501
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Не имеет отношения к вашей проблеме, но шаблон подразумевает inline, и вам не нужен static. Я действительно думаю, что с static вы нарушаете ODR. Почему вы их используете? Также вы можете сделать функции constexpr. Дальнейшее улучшение заключается в использовании (пересылке) ссылок и std::forward_as_tuple. Вот базовая реализация:

template <class... Args, std::size_t... Is>
constexpr auto init_helper(std::tuple<Args...> tp, std::index_sequence<Is...>)
{
    return std::tuple{std::get<Is>(tp)...};
}

template <class... Args>
constexpr auto init(std::tuple<Args...> tp)
{
    return init_helper(tp, std::make_index_sequence<sizeof...(Args) - 1>{});
}
auto test()
{
    static_assert(init(std::tuple{1})       == std::tuple{});
    static_assert(init(std::tuple{1, 2})    == std::tuple{1});
    static_assert(init(std::tuple{1, 2, 3}) == std::tuple{1, 2});
}

В комментариях вы сказали, что хотите увидеть, возможно ли, что init(std::tuple{1,2}) вернет значение напрямую, а не кортеж из одного значения. С учетом того, что я бы не рекомендовал это, так как это приведет к непоследовательному поведению функции, да, это возможно. И C++17 делает это чрезвычайно чистым:

template <class... Args>
constexpr auto init(std::tuple<Args...> tp)
{
    if constexpr (sizeof...(Args) == 2)
        return std::get<0>(tp);
    else
        return init_helper(tp, std::make_index_sequence<sizeof...(Args) - 1>{});
}

auto test()
{
    static_assert(init(std::tuple{1})       == std::tuple{});
    static_assert(init(std::tuple{1, 2})    == 1);
    static_assert(init(std::tuple{1, 2, 3}) == std::tuple{1, 2});
}

Разве constexpr не подразумевается? В любом случае, я согласен, и это кажется хорошим решением, но как вы думаете, я могу получить static_assert(init(std::tuple{1,2}) == 1) вместо унарного кортежа?

rausted 12.08.2018 18:46

@rausted нет, constexpr не подразумевается. (я думаю, кроме лямбд, начиная с C++ 17). Да, вы можете получить это, но вы уверены, что хотите этого? Было бы несоответствие. Я обновлю свой ответ на этот случай.

bolov 12.08.2018 18:48

Я согласен, что это непоследовательно, поскольку теперь init может возвращать как std::tuple<Args...>, так и общий T, но это интересный эксперимент с зависимой типизацией в метапрограммировании, если это возможно.

rausted 12.08.2018 18:50

Идея заключалась бы в реализации вспомогательной функции, которая имела бы список индексов элементов исходного кортежа для копирования:

#include <tuple>
#include <utility>
#include <cstddef>

template<typename x_Tuple, ::std::size_t... x_index> auto
make_tuple_helper(x_Tuple const & other, ::std::index_sequence<x_index...>)
{
    return ::std::make_tuple(::std::get<x_index>(other)...);
}

template<typename... x_Field> inline auto
cut_last_item(::std::tuple<x_Field...> const & other)
{
    return make_tuple_helper(other, ::std::make_index_sequence<sizeof...(x_Field) - ::std::size_t{1}>{});
}

template<> inline auto
cut_last_item(::std::tuple<> const & other)
{
    return other;
}

int main()
{
    ::std::tuple<int, short, float, double> t4{};
    ::std::tuple<int, short, float> t3{cut_last_item(t4)};
    ::std::tuple<int, short> t2{cut_last_item(t3)};
    ::std::tuple<int> t1{cut_last_item(t2)};
    ::std::tuple<> t0{cut_last_item(t1)};
    ::std::tuple<> t00{cut_last_item(t0)};
}

онлайн-компилятор

Почему вы используете ::std?

Evg 12.08.2018 20:26

@Evgeny Это относится к пространству имен std в глобальном пространстве имен

user7860670 12.08.2018 20:31

Я это понимаю. Но что могло бы пойти не так, если бы это был просто std? Когда мне следует использовать ::std вместо std?

Evg 12.08.2018 20:35

@Evgeny Идентификатор std не является специальным или зарезервированным. Просто std может относиться к чему-то объявленному во включающей области (например, к другому пространству имен, или классу, или перечислителю). В общем, рекомендуется всегда использовать префикс :: к именам из глобального пространства имен, чтобы они не меняли свое значение внезапно.

user7860670 12.08.2018 20:43

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