Может кто-нибудь объяснить трюк с индексами?

Я заметил, что «трюк с индексами» упоминается в контексте красивых печатных кортежей. Звучало интересно, поэтому я перешел по ссылке.

Ну, это не пошло хорошо. Я понял вопрос, но действительно не мог понять, что происходит. Зачем вообще нужны индексы чего-либо? Как различные функции, определенные там, помогают нам? Что такое «голый»? и Т. Д.

Может ли кто-нибудь дать пошаговое описание этой вещи для тех, кто не является экспертом по пакетам параметров и вариативным кортежам?


person einpoklum    schedule 16.07.2015    source источник


Ответы (1)


Проблема в том, что у нас есть std::tuple<T1, T2, ...> и некоторая функция f, которую мы можем вызывать для каждого элемента, где f возвращает int, и мы хотим сохранить эти результаты в массиве.

Начнем с конкретного случая:

template <typename T> int f(T ) { return sizeof(T); }

std::tuple<int, char, double> tup{42, 'x', 3.14};
std::array<int, 3> arr{ f(std::get<0>(tup)), 
                        f(std::get<1>(tup)),
                        f(std::get<2>(tup)) );

За исключением того, что запись всех этих get в лучшем случае неудобна и избыточна, а в худшем - подвержена ошибкам.

Сначала нам нужно включить заголовок утилиты для std::index_sequence и std::make_index_sequence:

#include <utility>

Теперь предположим, что у нас есть тип index_sequence<0, 1, 2>. Мы могли бы использовать это, чтобы свернуть эту инициализацию массива в расширение вариационного пакета:

template <typename Tuple, size_t... Indices>
std::array<int, sizeof...(Indices)> 
call_f_detail(Tuple& tuple, std::index_sequence<Indices...> ) {
    return { f(std::get<Indices>(tuple))... };
}

Это потому, что внутри функции f(std::get<Indices>(tuple))... расширяется до f(std::get<0>(tuple)), f(std::get<1>(tuple)), f(std::get<2>(tuple)). Это именно то, что мы хотим.

Последней деталью проблемы является просто создание этой конкретной последовательности индексов. В C++14 есть такая утилита с именем make_index_sequence.

template <typename Tuple>
std::array<int, std::tuple_size<Tuple>::value>
call_f(Tuple& tuple) {
    return call_f_detail(tuple,
        // make the sequence type sequence<0, 1, 2, ..., N-1>
        std::make_index_sequence<std::tuple_size<Tuple>::value>{}
        );
}

тогда как статья, на которую вы ссылаетесь, просто объясняет, как можно реализовать такую ​​​​метафункцию.

Bare, вероятно, что-то вроде ответа Люка Дантона:

template<typename T>
using Bare = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
person Barry    schedule 16.07.2015
comment
Bare кажется чем-то вроде remove_reference_t. Обратите внимание, что код на связанной странице OP принимает кортеж путем пересылки ссылки, поэтому Tuple может быть ссылочным типом, а tuple_size не работает со ссылочными типами. (Технически remove_cv не нужен. Предполагается, что tuple_size отлично работает с tuple, квалифицированными cv.) - person T.C.; 16.07.2015
comment
Отличный ответ, как обычно. Я нашел возможное определение Bare в этом ответе, что-то вроде decay_t. - person Alejandro; 16.07.2015
comment
В определении call_f_detail вы не имеете в виду f(std::get<Indices>(tuple)...), а не f(std::get<Indices>(tuple))...? - person einpoklum; 16.07.2015
comment
Кроме того, более существенный вопрос — зачем нам гибкость «поддержки» любой последовательности индексов? Разве индексы‹0, 1, 2› не включают ненужную избыточность? - person einpoklum; 16.07.2015
comment
@einpoklum Нет, не знаю. Расширение пакета работает, беря выражение слева от ..., поэтому первое выражение будет ожидать f(std::get<0>(tuple), std::get<1>(tuple), ...) вместо f(std::get<0>(tuple)), f(std::get<1>(tuple)), .... Мы хотим вызывать f для каждого элемента отдельно, а не для всех вместе. См. мой ответ здесь о расширении различных выражений пакета. - person Barry; 16.07.2015
comment
Я считаю, что это правильно, как написано. f(std::get<Indices>(tuple)...) расширяется до f(42, 'x', 3.14) с образцом кортежа, упомянутым в ответе, но f(std::get<Indices>(tuple))... расширяется до f(42), f('x'), f(3.14) - person celticminstrel; 16.07.2015
comment
@einpoklum Я не понимаю другого вашего вопроса. Какая избыточность? - person Barry; 16.07.2015
comment
(1) Повторная избыточность: значения аргументов шаблона точно такие же, как их индексы в последовательности аргументов шаблона. В обычном (не мета) программировании вы обычно избегаете использования size_t a[3] = { 0, 1, 2}, так как это глупо. (2) Итак, index_sequence действительно существует в стандартной библиотеке, но не содержит индексов, последовательностей или чего-либо еще? (3) Нельзя ли как-нибудь использовать std::index_sequence_for? - person einpoklum; 16.07.2015
comment
@einpoklum (1) Вот почему мы используем make_index_sequence для создания index_sequence? (2) Они все есть, см. ссылку в моем ответе. (3) Можно, но в любом случае это реализовано с точки зрения make_index_sequence, так какая разница? - person Barry; 17.07.2015
comment
(2) Как очень понятно. Нет. :-( - person einpoklum; 17.07.2015