ideas
ideas copied to clipboard
Неявное приведение std::pair к структуре.
Неявное приведение 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();
может вызывать неоднозначность.
Если неявное приведение 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}))
?
Как предлагается обрабатывать случаи, когда существует конструктор копирования/перемещения для кортежа?
По поводу приведения 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};
Можно определить небольшой конвертер:
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());
есть такой вариант. Я нечто похожее у себя писал.
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>();
Возможно, стоит обьединить с #485, где более жёсткие требования: делать неявное приведение и для массивов, передвигаемых std::memcpy
.