ideas
ideas copied to clipboard
Добавить operator[] для tuple.
Идея
Предлагаю добавить функцию operator[](size_t) в класс std::tuple. Функция, в отличии от std::get, принимает аргумент - индекс значения.
Это очень упростит пользовательский код т.к. постоянно набирать std::get<>(tuple) - жутко не удобно.
Пример:
std::tuple<int, double> tp(1, 3.14);
for(size_t i = 0; i < tp.size();++i) {
std::cerr << tp[0] << '\n';
}
Тоже самое, сейчас:
std::apply([](auto&&... xs) {
((std::cout << std::forward<decltype(xs)>(xs) << '\n'), ...); },
tp);
Хотелось бы увидеть пример реализации этой функции (или, по крайней мере, её объявление)
Что-то подобное можно реализовать только для std::tuple<T, T, ..., T>
Что-то подобное можно реализовать только для
std::tuple<T, T, ..., T>
Чисто теоретически, то можно реализовать возвращая std::variant<A,B,C,T>\ANY из оператора [], вот только код легче не станет.
Это можно было бы элегантно решить если бы параметр функции consteval функции был бы constant expression.
Уже есть бумага https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1045r0.html, где предлагаю ввести constexpr для аргумента функции.
void f(constexpr int x) {
static_assert(x == 5);
}
Кто нибудь знает, если по этому вопросу продвижение?
И ещё вопрос, почему для consteval функций аргументы не являются constant expression?
Т.е. в чём сложность сделать след. код компилируемым?
consteval void f(int x) {
static_assert(x == 5);
}
Таким образом предлагаемый мной operator[] должен быть consteval. Иначе, как выше заметили это не реализовать или не имеет практического смысла.
Что то похожее спрашивают на SO https://stackoverflow.com/questions/56130792/will-consteval-functions-allow-template-parameters-dependent-on-function-argumen.
Если хочется только упростить синтаксис, то можно написать свою функцию forEach:
#include <cstddef>
#include <utility>
#include <tuple>
#include <iostream>
template<class Functor, class... Args>
inline void forEach(std::tuple<Args...> &tuple, Functor f) {
impl::forEach(tuple, f, std::make_integer_sequence<sizeof...(Args)>{});
}
// перегрузки
namespace impl {
template<class Functor, class... Args, std::size_t... Is>
void forEach(std::tuple<Args...> &tuple, Functor f, std::integer_sequence<Is...>) {
(f(tuple.get<Is>, Is), ...);
}
// перегрузки
}
void test() {
std::tuple<int, std::string> tuple { 10, "Hello, World!" };
forEach(tuple, [](auto obj, auto i) {
std::cout << "Iteration: " << i << std::endl
<< "Data: " << obj << std::endl;
});
}
Если это требуется для обращения по индексам, которые известны только в рантайме, проще будет написать свой tuple.
Проблема заключается в том, что tuple'ы обычно реализуются наследованием от n структур, которые называют tuple leaf'ами, следовательно, доступ к m-ому элементу требует static_cast'а к какому-нибудь условному
(namespace)::(странное имя)::__tuple_leaf<(namespace)::(странное имя)::__tuple_traits::type_table<m>, m>, а m является параметром шаблона, она должна быть известна в compile-time'е.
Что же делать? Пусть tuple будет состаять из сырого байтового массива размером в (sizeof(Pack) + ... + 0), где Pack - наши типы (компилятор все выровняет сам). И вызывать конструкторы и деструкторы вручную. А таблицу offset'ов иметь в рантайме, по хешу имени типа. И с индесами тоже самое. Правда тип для поиска по индеку все рано придется указать явно.
А следующий вариант подойдёт:
auto [... elements] = some_tuple;
elemets...[I] // <- можно обращаться по индексу