Ответ 1
Общепринято использовать кортежи, чтобы облегчить прохождение вариационного количества элементов (в этом случае параметры для перехода на emplace_back
), немного техники распакуйте кортеж назад. Таким образом, можно написать утилиту back_emplacer
, требуя от пользователя использовать функции tuple factory (один из std::make_tuple
, std::tie
, std::forward_as_tuple
), где это имеет смысл:
#include <type_traits>
#include <tuple>
// Reusable utilites
template<typename T>
using RemoveReference = typename std::remove_reference<T>::type;
template<typename T>
using Bare = typename std::remove_cv<RemoveReference<T>>::type;
template<typename Out, typename In>
using WithValueCategoryOf = typename std::conditional<
std::is_lvalue_reference<In>::value
, typename std::add_lvalue_reference<Out>::type
, typename std::conditional<
std::is_rvalue_reference<Out>::value
, typename std::add_rvalue_reference<Out>::type
, Out
>::type
>::type;
template<int N, typename Tuple>
using TupleElement = WithValueCategoryOf<
typename std::tuple_element<N, RemoveReference<Tuple>>::type
, Tuple
>;
// Utilities to unpack a tuple
template<int... N>
struct indices {
using next = indices<N..., sizeof...(N)>;
};
template<int N>
struct build_indices {
using type = typename build_indices<N - 1>::type::next;
};
template<>
struct build_indices<0> {
using type = indices<>;
};
template<typename Tuple>
constexpr
typename build_indices<std::tuple_size<Bare<Tuple>>::value>::type
make_indices() { return {}; }
template<typename Container>
class back_emplace_iterator {
public:
explicit back_emplace_iterator(Container& container)
: container(&container)
{}
template<
typename Tuple
// It important that a member like operator= be constrained
// in this case the constraint is delegated to emplace,
// where it can more easily be expressed (by expanding the tuple)
, typename = decltype( emplace(std::declval<Tuple>(), make_indices<Tuple>()) )
>
back_emplace_iterator& operator=(Tuple&& tuple)
{
emplace(*container, std::forward<Tuple>(tuple), make_indices<Tuple>());
return *this;
}
template<
typename Tuple
, int... Indices
, typename std::enable_if<
std::is_constructible<
typename Container::value_type
, TupleElement<Indices, Tuple>...
>::value
, int
>::type...
>
void emplace(Tuple&& tuple, indices<Indices...>)
{
using std::get;
container->emplace_back(get<Indices>(std::forward<Tuple>(tuple))...);
}
// Mimic interface of std::back_insert_iterator
back_emplace_iterator& operator*() { return *this; }
back_emplace_iterator& operator++() { return *this; }
back_emplace_iterator operator++(int) { return *this; }
private:
Container* container;
};
template<typename Container>
back_emplace_iterator<Container> back_emplacer(Container& c)
{ return back_emplace_iterator<Container> { c }; }
Демонстрация кода доступна. В вашем случае вы хотите вызвать std::fill_n(back_emplacer(v), 10, std::forward_as_tuple(1, 1.0));
(std::make_tuple
также допустимо). Вы также хотите, чтобы обычный материал итератора сделал эту функцию полной - я рекомендую Boost.Iterators для этого.
Я должен действительно подчеркнуть, что такая утилита не приносит большого значения при использовании с std::fill_n
. В вашем случае это сохранит конструкцию временного Foo
, в пользу кортежа ссылок (кортеж значений, если вы должны использовать std::make_tuple
). Я оставляю его читателю, чтобы найти другой алгоритм, в котором back_emplacer
был бы полезен.