Возможное решение:
template<std::size_t i>
using index = std::integral_constant<std::size_t, i>;
template<std::size_t N, class Tuple, typename S>
auto replace_tuple_element(Tuple&& tuple, S&& s) {
auto get_element = [&tuple, &s]<std::size_t i>(Index<i>) {
if constexpr (i == N)
return std::forward<S>(s);
else
return std::get<i>(std::forward<Tuple>(tuple));
};
using T = std::remove_reference_t<Tuple>;
return [&get_element]<std::size_t... is>(std::index_sequence<is...>) {
return std::make_tuple(get_element(index<is>{})...);
}(std::make_index_sequence<std::tuple_size_v<T>>{});
}
Обратите внимание, что это приводит к разложению всех типов элементов, удаляя ссылки и константы.
Эта поправка частично решает эту проблему:
template<std::size_t N, class Tuple, typename S>
auto replace_tuple_element(Tuple&& tuple, S&& s) {
using T = std::remove_reference_t<Tuple>;
auto get_element = [&tuple, &s]<std::size_t i>(index<i>) {
if constexpr (i == N)
return std::forward<S>(s);
else
if constexpr (std::is_lvalue_reference_v<std::tuple_element_t<i, T>>)
return std::ref(std::get<i>(std::forward<Tuple>(tuple)));
else
return std::get<i>(std::forward<Tuple>(tuple));
};
return [&get_element]<std::size_t... is>(std::index_sequence<is...>) {
return std::make_tuple(get_element(index<is>{})...);
}(std::make_index_sequence<std::tuple_size_v<T>>{});
}
Теперь replace_tuple_element
также следует соглашению std::make_tuple
, которое преобразует std::reference_wrapper
аргументы в ссылки. Он сохраняет ссылочные типы, но сбрасывает константу верхнего уровня.
struct Foo {
Foo(int i) : value(i) {}
int value;
};
int main() {
int i = 1;
int j = 2;
auto t1 = std::make_tuple(std::make_unique<Foo>(0), std::ref(i), std::cref(j), 4);
static_assert(std::is_same_v<decltype(t1),
std::tuple<std::unique_ptr<Foo>, int&, const int&, int>>);
auto t2 = replace_tuple_element<1>(std::move(t1), std::make_unique<Foo>(5));
static_assert(std::is_same_v<decltype(t2),
std::tuple<std::unique_ptr<Foo>, std::unique_ptr<Foo>, const int&, int>>);
auto t3 = replace_tuple_element<0>(std::move(t2), std::cref(i));
static_assert(std::is_same_v<decltype(t3),
std::tuple<const int&, std::unique_ptr<Foo>, const int&, int>>);
auto t4 = replace_tuple_element<2>(std::move(t3), i);
static_assert(std::is_same_v<decltype(t4),
std::tuple<const int&, std::unique_ptr<Foo>, int, int>>);
}
Полная демонстрация с утверждениями времени выполнения