Передать std::tuple с вариативным параметром шаблона в качестве типа его элементов другой функции в виде списка параметров

Я реализовал свою версию std::map, чтобы лучше понять, как все работает изнутри. При реализации std::map::emplace() я столкнулся с проблемой.

Итак, моя сигнатура функции выглядит так:

template <typename Key, typename Value>
template <typename ... Args1, typename ... Args2>
std::pair<typename Map<Key, Value>::Iterator, bool> Map<Key, Value>::emplace(
    std::piecewise_construct_t pwc,
    std::tuple<Args1...> first_args,
    std::tuple<Args2...> second_args);

Перед фактическим размещением мне нужно создать ключ из first_args, чтобы сравнить ключи в дереве. Я пробовал несколько вещей, но не могу понять, как правильно это сделать. Как я понимаю, это должно выглядеть примерно так:

Key k(std::get<sizeof...(Args1)>(std::forward<Args1>(first_args));

Проблема в том, что для каждого элемента кортежа std::get() должен получать разные числа в качестве параметра своего шаблона (чтобы правильный элемент кортежа передавался в правильном месте).

Я видел, как люди решают эту проблему, используя размер в качестве параметра шаблона и передавая std::index_sequence в качестве одного из параметров, но std::map::emplace() не имеет такого подхода, поэтому должен быть способ реализовать внедрение без этого.

Заранее спасибо. Любой совет будет оценен!


person spandei    schedule 25.06.2019    source источник
comment
Ваша сигнатура функции отличается от сигнатуры std::map, поэтому std::map::emplace() не имеет такого подхода. Взгляните на это. Если вам абсолютно необходимо использовать кортеж, вы можете использовать std::make_from_tuple. Это функция библиотеки C++17, но ее можно легко реализовать в C++14 с помощью std::index_sequence.   -  person Indiana Kernick    schedule 25.06.2019
comment
std::map::emplace передает работу конструктору std::pair.   -  person Indiana Kernick    schedule 25.06.2019
comment
Вы проверили страницу, на которую я дал ссылку? std::pair имеет конструктор, который принимает std::piecewise_construct_t, std::tuple<Args1...> first_args, std::tuple<Args2...> second_args, который сделает всю работу за вас.   -  person Indiana Kernick    schedule 25.06.2019
comment
@ Kerndog73 О, так что мы можем просто перенаправить параметры в случае std:: piecewise_construct в конструктор std::pair. Так же, как и в других случаях. Спасибо. Единственное, чего я не понял, это то, что у нас должен быть ключ перед размещением, чтобы пройти по дереву и найти, где разместить элемент. В случае, если ссылка на ключ не указана, как мы будем сравнивать ключ и ключи элементов карты, не создавая его перед созданием фактического узла?   -  person spandei    schedule 25.06.2019
comment
Создайте пару из аргументов: std::pair<Key, Value> pair(std::forward<Args>(args)...) (позволив std::pair разобраться со всеми сложными деталями). Используйте pair.first, чтобы выяснить, куда вам нужно его поместить. Затем std::move туда, где он должен быть.   -  person Indiana Kernick    schedule 25.06.2019
comment
Я настоятельно рекомендую вам прочитать страницу cppreference, чтобы узнать подробности о std::map .   -  person Indiana Kernick    schedule 25.06.2019
comment
@ Kerndog73, но тогда мы теряем всю идею emplace? Описанный способ аналогичен вставке, за исключением того, что мы создаем пару любым способом, который указал пользователь. Из описания std::map::emplace(): осторожное использование emplace позволяет создать новый элемент, избегая ненужных операций копирования или перемещения. Если мы сначала создадим весь узел, у нас будут ненужные операции перемещения.   -  person spandei    schedule 25.06.2019
comment
Я не знаком с деталями, но я уверен, что std::map реализовано с помощью какого-то дерева. Узлы дерева распределяются динамически, поэтому вы можете построить пару непосредственно в узле, когда вы его выделяете. Затем вам просто нужно возиться с указателями, чтобы вставить узел в дерево. Пара будет построена непосредственно в узле, поэтому не будет лишних копий или перемещений.   -  person Indiana Kernick    schedule 25.06.2019


Ответы (1)


Я видел, как люди решают эту проблему, используя размер в качестве параметра шаблона и передавая std::index_sequence в качестве одного из параметров, но std::map::emplace() не имеет такого подхода, поэтому должен быть способ внедрить без этого.

Это правда, что std::map::emplace() не получает std::index_sequence, но я не знаю, создать ли внутри std::index_sequence и вызвать вспомогательную функцию для правильного управления кортежем.

Другими словами, вы можете написать что-то вроде следующего

Key k { make_object_from_tuple<Key>(first_arg, std::index_sequence_for<Args1...>{}) };

а внутри make_object_from_tuple() вы можете использовать std::index_sequence для извлечения элемента из кортежа и создания объекта Key.

Другими словами: как предложил Kerndog73, вы можете скопировать реализацию std::make_from_tuple_impl() на этой странице.

Если вы не хотите разрабатывать новую функцию, вы можете использовать кусочный конструктор из std::pair.

Ничто не заставляет вас построить std::pair<Key, Value>: если вы хотите построить сначала Key и, только если необходимо, затем Value, вы можете построить перед std::pair<Key, int> и после (в случае) std::pair<Value, int>.

Я имею в виду... вы можете создать Key без Value

std::tuple<int> ti{0};

Key k { std::pair<Key, int>{std::piecewise_construct_t, first_args, ti).first };

и после, только если вам это нужно, Value

Value v { std::pair<Value, int>{std::piecewise_construct_t, second_args, ti).first };
person max66    schedule 25.06.2019
comment
Это именно то, что я искал! Теперь, когда я смог создать объект Key из кортежа, остальное тривиально. Это работает, большое спасибо! - person spandei; 25.06.2019