cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
89 stars 0 forks source link

Добавить operator[] для tuple. #557

Open blacktea opened 1 year ago

blacktea commented 1 year ago

Идея Предлагаю добавить функцию 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);
Smertig commented 1 year ago

Хотелось бы увидеть пример реализации этой функции (или, по крайней мере, её объявление)

tomilov commented 1 year ago

Что-то подобное можно реализовать только для std::tuple<T, T, ..., T>

sergii-rybin-tfs commented 1 year ago

Что-то подобное можно реализовать только для std::tuple<T, T, ..., T>

Чисто теоретически, то можно реализовать возвращая std::variant<A,B,C,T>\ANY из оператора [], вот только код легче не станет.

blacktea commented 1 year ago

Это можно было бы элегантно решить если бы параметр функции 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.

xiran56 commented 8 months ago

Если хочется только упростить синтаксис, то можно написать свою функцию 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'ов иметь в рантайме, по хешу имени типа. И с индесами тоже самое. Правда тип для поиска по индеку все рано придется указать явно.

apolukhin commented 3 months ago

А следующий вариант подойдёт:

auto [... elements] = some_tuple;
elemets...[I] // <- можно обращаться по индексу