ideas icon indicating copy to clipboard operation
ideas copied to clipboard

Неявное приведение std::pair к структуре.

Open IvanOrfanidi opened this issue 1 year ago • 5 comments

Неявное приведение std::pair к структуре.

Возможное продолжение с structured binding и упрощение синтаксиса С++. Приведение будет работать если типы структуры и pair совпадают, ну и логично количество элементов только 2.

Например:

int main() {
    std::map<std::string, unsigned> all_data = {
      {"GPS", 14},
      {"GLONASS", 10},
      {"GALILEO", 8},
    };

    struct GPS_Data {
        std::string service{};
        unsigned num_of_sat = 0;
    };
    GPS_Data gps = *all_data.begin();

    std::cout << "Service: " << gps.service << ", Number of satellites: " << gps.num_of_sat << std::endl;
}

Вместо:

    const auto& [service, num_of_sat] = *all_data.begin();
    GPS_Data gps = {service, num_of_sat};

Возможные проблемы: Пониманием кода когда структура содержит один элемент std::pair.

    struct GPS_Data {
        std::pair<std::string, unsigned> data;
    };
    GPS_Data gps = {*all_data.begin()};

Тогда запись: GPS_Data gps = {*all_data.begin()}; против GPS_Data gps = *all_data.begin(); может вызывать неоднозначность.

IvanOrfanidi avatar Aug 22 '22 23:08 IvanOrfanidi

Если неявное приведение std::pair — то и std::tuple тоже? К чему ограничение на два элемента?

Предполагается, что GPS_Data gps = *all_data.begin() семантически эквивалентно GPS_Data gps = {std::get<0>(begin_element), std::get<1>(begin_element}), а GPS_Data gps = std::move(*all_data.begin()} таким образом должно быть семантически эквивалентно GPS_Data gps = {std::get<0>(std::move(begin_element)), std::get<1>(std::move(begin_element}))?

Как предлагается обрабатывать случаи, когда существует конструктор копирования/перемещения для кортежа?

eoan-ermine avatar Aug 25 '22 19:08 eoan-ermine

По поводу приведения std::tuple хорошая идея. Если честно не знаю можно ли на этапе компиляции выяснить кол-во и тип std::tuple не будет ли в этом скрытых проблем.

По поводу конструктор копирования/перемещения, можно сделать как это сделано в structured binding. по ссылке const auto& [service, num_of_sat] = *all_data.begin(); или по значению auto [service, num_of_sat] = *all_data.begin(); Там же нет проблем с конструктором копирования/перемещения для std::pair.

Речь идет просто о "синтаксическом сахаре", чтобы не писать отдельный конструктор копирования/перемещения для структуры. Или не пользоваться дополнительными объявлениями переменных как ниже:

const auto& [service, num_of_sat] = *all_data.begin();
GPS_Data gps = {service, num_of_sat};

или так

GPS_Data gps = {all_data.begin()->first, all_data.begin()->second};

IvanOrfanidi avatar Aug 26 '22 15:08 IvanOrfanidi

Можно определить небольшой конвертер:

template<typename T, typename ... Args, size_t ... Indices>
T tuple_cast_impl(std::index_sequence<Indices...>, const std::tuple<Args...>& t)
{
    return T{std::get<Indices>(t)...};
}

template<typename T, typename ... Args, size_t ... Indices>
T tuple_cast_impl(std::index_sequence<Indices...>, const std::pair<Args...>& t)
{
    return T{std::get<Indices>(t)...};
}

template<typename T, typename ... Args>
T tuple_cast(const std::tuple<Args...>& t)
{
    return tuple_cast_impl<T>(std::make_index_sequence<sizeof...(Args)>{}, t);
}

template<typename T, typename ... Args>
T tuple_cast(const std::pair<Args...>& t)
{
    return tuple_cast_impl<T>(std::make_index_sequence<sizeof...(Args)>{}, t);
}

и тогда с ним будет работать, при необходимости можно перенести в конструктор.

    auto gps = tuple_cast<GPS_Data>(*all_data.begin());

pavelkryukov avatar Aug 27 '22 09:08 pavelkryukov

есть такой вариант. Я нечто похожее у себя писал.


template <typename S>
struct cast {
    cast(const S& s)
        : s {s} { }
    template <typename T>
    operator T() const { return impl<T>(std::make_index_sequence<std::tuple_size_v<S>> {}); }
    template <typename T>
    T to() const { return impl<T>(std::make_index_sequence<std::tuple_size_v<S>> {}); }

private:
    const S& s;
    template <typename T, size_t... Is>
    T impl(std::index_sequence<Is...>) const { return {std::get<Is>(s)...}; }
};

GPS_Data gps; 

gps = cast(variable);
auto gps2 = cast(variable).to<GPS_Data>();

XRay3D avatar Aug 28 '22 22:08 XRay3D

Возможно, стоит обьединить с #485, где более жёсткие требования: делать неявное приведение и для массивов, передвигаемых std::memcpy.

pavelkryukov avatar Aug 29 '22 07:08 pavelkryukov