std::pair 沮丧的作业
std::pair assignment with downcast
对于事件反应器和前导器中的超时,我使用了一个优先级队列,它也允许 O(log(n)) 随机访问删除事件(当事件是 signalled/completes 而不是超时发生时)。我存储 std::pair<std::chrono::steady_clock::time_point, Timed *>
,其中 Timed
是一个 class,它添加了一个索引(指向队列)以允许在调用 TimedQ::Remove(Timed *p)
时有效地删除。当我想要一个与超时关联的事件类型时,我从 Timed
派生。队列的Top()
和Pop()
return一对。
以前有一堆代码使用队列比如
std::tie(timePt0, eventPtr0) = timeoutQ.Pop();
std::tie(timePt1, eventPtr1) = std::move(hold);
在我开始在队列中使用基础 class Timed *
而不是特定事件类型(即 Timed
最初是模板化类型)之前,它工作得很好,因为我最终需要支持可以与超时关联的多种不同事件类型。但是,由于 eventPtr*
是派生类型(我可以 static_cast
从 Timed *
return 由队列编辑),上面的代码不再有效。
我想知道执行此操作的最佳方法是什么。现在,它最终变得非常冗长,而且我也担心创建临时文件等效率问题:
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
我唯一的另一个想法是通过派生事件 return 对一对函数进行模板化 class,但从代码大小的角度来看,这似乎很糟糕,因为这些函数的多个实例即使机器代码应该相同,也会被创建,因为在所有情况下它都是一个存储的指针。
编辑:
我也试过使用这个,编译,但我不确定是否正确或有效:
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);
您 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 可以复制每个两次,但同样,只有在没有启用优化的情况下)。
对于事件反应器和前导器中的超时,我使用了一个优先级队列,它也允许 O(log(n)) 随机访问删除事件(当事件是 signalled/completes 而不是超时发生时)。我存储 std::pair<std::chrono::steady_clock::time_point, Timed *>
,其中 Timed
是一个 class,它添加了一个索引(指向队列)以允许在调用 TimedQ::Remove(Timed *p)
时有效地删除。当我想要一个与超时关联的事件类型时,我从 Timed
派生。队列的Top()
和Pop()
return一对。
以前有一堆代码使用队列比如
std::tie(timePt0, eventPtr0) = timeoutQ.Pop();
std::tie(timePt1, eventPtr1) = std::move(hold);
在我开始在队列中使用基础 class Timed *
而不是特定事件类型(即 Timed
最初是模板化类型)之前,它工作得很好,因为我最终需要支持可以与超时关联的多种不同事件类型。但是,由于 eventPtr*
是派生类型(我可以 static_cast
从 Timed *
return 由队列编辑),上面的代码不再有效。
我想知道执行此操作的最佳方法是什么。现在,它最终变得非常冗长,而且我也担心创建临时文件等效率问题:
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
我唯一的另一个想法是通过派生事件 return 对一对函数进行模板化 class,但从代码大小的角度来看,这似乎很糟糕,因为这些函数的多个实例即使机器代码应该相同,也会被创建,因为在所有情况下它都是一个存储的指针。
编辑: 我也试过使用这个,编译,但我不确定是否正确或有效:
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);
您 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 可以复制每个两次,但同样,只有在没有启用优化的情况下)。