std :: pair присвоение с понижением

Для таймаутов в реакторах событий и проакторах я использую очередь с приоритетами, которая также позволяет O (log (n)) произвольно удалять события (когда событие сигнализируется / завершается, а не происходит тайм-аут). Я храню std::pair<std::chrono::steady_clock::time_point, Timed *> где Timed класс, который добавляет, имеет индекс (указывающий на очередь), чтобы обеспечить эффективное удаление при вызове TimedQ::Remove(Timed *p), Когда я хочу, чтобы тип события был связан с тайм-аутом, я получаю из Timed, Очередь Top() а также Pop() вернуть пару.

Я имел обыкновение иметь кучу кода, использующего очередь, такую ​​как

std::tie(timePt0, eventPtr0) = timeoutQ.Pop();
std::tie(timePt1, eventPtr1) = std::move(hold);

который работал нормально, прежде чем я начал использовать базовый класс Timed * в очереди вместо определенного типа события (т.е. Timed изначально был вместо этого шаблонным типом), так как в конечном итоге мне нужно было поддерживать несколько различных типов событий, которые могут быть связаны с таймаутами. Однако с eventPtr* будучи производным типом (что я могу static_cast от Timed * возвращается в очередь), код, подобный приведенному выше, больше не работает.

Мне интересно, как лучше это сделать. Прямо сейчас, это закончилось очень многословно, и я также обеспокоен эффективностью, такой как создание временных:

auto v(timeoutQ.Pop());
timePt0 = v.first;
eventPtr0 = static_cast<TimedEvent *>(v.second);
std::tie(timePt1, eventPtr1) = std::move(std::make_pair(hold.first, static_cast<TimedEvent *>(hold.second)); // I didn't literally do it like this, but I'm just trying to illustrate my struggle

Единственная другая идея, которая у меня была, заключалась в том, чтобы шаблонировать функции, которые возвращают пару по производному классу событий, но это кажется плохим с точки зрения размера кода, так как будет создано несколько экземпляров этих функций, даже если машинный код должен быть идентичен, поскольку во всех случаях это указатель, который хранится.


Редактировать:
Я также попытался использовать это, которое компилируется, но я не уверен, что это правильно или эффективно:

template<class D>
std::pair<std::chrono::steady_clock::time_point, D *> &&Cnvrt(std::pair<std::chrono::steady_clock::time_point, Timed *> &&in)
{
return std::make_pair(in.first, static_cast<D *>(in.second));
}

Первоначальный пример тогда стал бы

std::tie(timePt0, eventPtr0) = Cnvrt<std::remove_pointer<decltype(eventPtr0)>::type>(timeoutQ.Pop());
std::tie(timePt1, eventPtr1) = Cnvrt<std::remove_pointer<decltype(eventPtr1)>::type>(hold);

1

Решение

Cnvrt вы показали, возвращает висячую ссылку — классический UB.

Вот исправленная C ++ 11-совместимая версия, которая также проверяет D во время компиляции и устраняет необходимость в руководстве std::remove_pointer<...>::type на сайте вызова:

template<typename D>
constexpr
std::pair<std::chrono::steady_clock::time_point, D>
Cnvrt(std::pair<std::chrono::steady_clock::time_point, Timed*> const& in) noexcept
{
static_assert(std::is_pointer<D>{}, "D is not a pointer type");

using derived_type = typename std::remove_pointer<D>::type;
static_assert(std::is_base_of<Timed, derived_type>{}, "D does not derive from Timed");

using ptr_type = typename std::remove_cv<D>::type;
return {in.first, static_cast<ptr_type>(in.second)};
}

// ...

std::tie(timePt0, eventPtr0) = Cnvrt<decltype(eventPtr0)>(timeoutQ.Pop());
std::tie(timePt1, eventPtr1) = Cnvrt<decltype(eventPtr1)>(hold);

Демо онлайн

Вот реализация, которая должна работать на VC ++ 2012:

template<typename D>
std::pair<std::chrono::steady_clock::time_point, D>
Cnvrt(std::pair<std::chrono::steady_clock::time_point, Timed*> const& in) throw()
{
static_assert(std::is_pointer<D>::value, "D is not a pointer type");

typedef typename std::remove_pointer<D>::type derived_type;
static_assert(std::is_base_of<Timed, derived_type>::value, "D does not derive from Timed");

typedef typename std::remove_cv<D>::type ptr_type;
return std::make_pair(in.first, static_cast<ptr_type>(in.second));
}

Демо онлайн

Здесь нет никакой проблемы с эффективностью, даже ваша худший случай Сценарий, если компилятор не выполняет вообще никаких оптимизаций, является просто копией одного скаляра и одного указателя (VC ++ 2012 может копировать каждый дважды, но, опять же, только без включенной оптимизации).

1

Другие решения

Других решений пока нет …