Вы, кажется, заново изобретаете std::tuple
с худшим помощником std::make_tuple
. Используйте это вместо этого. Стандарт не дает гарантий сложности ни для конструктора переадресации std::tuple
, ни для std::make_tuple
, но это своего рода спорный вопрос, потому что эти два используют идеальную пересылку, поэтому для вызова элемента создается ровно одна конструкция перемещения / копирования / размещения на элемент std::make_tuple
; остальное перемешивает ссылки вокруг. Это линейное число конструкций по крайней мере.
Конечно, не гарантируется, что ваш компилятор будет корректно обрабатывать все перетасовки ссылок, но вы все равно оптимизируете на неправильном уровне.
В целях иллюстрации почти, но не совсем то, что происходит:
template<typename... T>
class tuple {
T... members; // this is not correct, only here for illustration
// The forwarding constructor
template<typename... U>
explicit
tuple(U&&... u)
: member(std::forward<U>(u)...)
{}
};
template<typename... T>
tuple<typename std::decay<T>::type...>
make_tuple(T&&... t)
{ return tuple<typename std::decay<T>::type...>(std::forward<T>(t)...); }
Таким образом, в вызове к auto tuple = std::make_tuple(1, 2, 3)
есть три временных int
s от вызова к make_tuple
, затем три int&&
x значения от первых вызовов к std::forward<int>(t)...
внутри make_tuple
, которые связываются с аргументы конструкторов, которые снова передаются в виде int&&
x значений для концептуальных трех членов std::tuple<int, int, int>
, которые построены из них.
Просто понял, что то, что я сказал о количестве конструкций, относится только к вызову std::make_tuple
, а не ко всему выражению auto tuple = std::make_tuple(...);
. Поскольку кортеж возвращается из функции, он может потребовать перемещения / RVO к конечной переменной. Он по-прежнему линейный, и его по-прежнему любят оптимизировать компиляторы, и не стоит беспокоиться об оптимизации.
Однако C ++ 0x настолько совершенен, что уже может делать то, что вы описываете в своем ответе:
int i = 3;
std::tuple<int, int, int> tuple = std::forward_as_tuple(1, 2, i);
Вызов forward_as_tuple
вернет std::tuple<int&&, int&&, int&>
. Этот помощник никогда не возвращает кортеж со значениями, только «мелкий» кортеж ссылок. Соответствующий конструктор преобразования std::tuple<int, int, int>
затем инициализирует свои «члены» двумя ходами и копией. Обратите внимание, что эта глупая (не) оптимизация ставит вас под угрозу написания auto tuple = std::forward_as_tuple(1, 2, i);
, который является кортежем с двумя висячими ссылками.