C ++ 17: при распаковке кортежа оставляйте только некоторые элементы - PullRequest
0 голосов
/ 04 мая 2018

Представим, что вам нужно вызвать следующий метод:

std::tuple<int, int, int> foo();

В C ++ 17 вы можете вызвать функцию и распаковать кортеж в одну строку:

auto [a, b, c] = foo();

Теперь, как мне перейти к сохранению только b и c и сбросу a?

В настоящее время мне известны только два варианта:


1 - я могу использовать фиктивную переменную при автоматической распаковке

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

#pragma warning(push)
#pragma warning(disable:4101)
// ReSharper disable once CppDeclaratorNeverUsed
auto [_, b, c] = foo();
#pragma warning(pop)

2 - я могу сохранить весь кортеж и использовать std::get, чтобы получить ссылку на единственные переменные, которые мне нужны. Код менее неприятен, но синтаксис также менее прост.

Более того, размер этого кода увеличивается на одну строку для каждого нового значения, которое мы хотим сохранить в кортеже.

auto tuple = foo();
int b = std::get<1>(tuple);
int c = std::get<2>(tuple);

Есть ли другой и более простой способ распаковать только некоторые параметры в кортеже?

Ответы [ 4 ]

0 голосов
/ 04 мая 2018

В MSVC уже исправлено в VS 15.7 Preview. Окончательный релиз 15.7 должен быть доступен в ближайшие недели. Это означает, что текущая логика, поддерживаемая последними выпусками всех основных компиляторов, выглядит следующим образом:

  • Если в объявлении структурированной привязки используется хотя бы одна из структурированных привязок, предупреждение «Неиспользуемая переменная» не будет выдано для других привязок в той же декларации.
  • Если ни одна из привязок в объявлении структурированной привязки не используется, можно отключить предупреждение, используя атрибут [[maybe_unused]]:

    [[maybe_unused]] auto [a, b, c] = foo();
0 голосов
/ 04 мая 2018

Другой альтернативой является использование std::tie:

int b, c;
std::tie(std::ignore, b, c) = foo();

Редактировать

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

  • Вывод типа невозможен
  • Объекты должны создаваться раньше, поэтому, если конструкторы по умолчанию не являются тривиальными, это не очень хорошая альтернатива.
0 голосов
/ 04 мая 2018

Вы можете написать вспомогательную функцию, которая возвращает вам только определенные индексы std::tuple:

template <size_t... Is, typename Tuple>
auto take_only(Tuple&& tuple) {
    using T = std::remove_reference_t<Tuple>;

    return std::tuple<std::tuple_element_t<Is, T>...>(
        std::get<Is>(std::forward<Tuple>(tuple))...);
}

auto [b, c] = take_only<1, 2>(foo());

Или уронить голову или что-то:

template <size_t... Is, typename Tuple>
auto drop_head_impl(Tuple&& tuple, std::index_sequence<0, Is...> ) {
    return take_only<Is...>(std::forward<Tuple>(tuple));
}

template <typename Tuple>
auto drop_head(Tuple&& tuple) {
    return drop_head_impl(std::forward<Tuple>(tuple),
        std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>());
}

auto [b, c] = drop_head(foo());

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

Так что просто делай то, что Витторио говорит :

auto [a, b, c] = foo();
(void)a;
0 голосов
/ 04 мая 2018

К сожалению структурированные привязки не поддерживают явным образом удаление элементов, и такие атрибуты, как [[maybe_unused]], нельзя применять к структурированным привязкам (для этого есть предложение: P0609 : «Атрибуты для структурированных привязок» ).

Вот возможное решение:

auto [a, b, c] = foo();
(void) a; // unused
...