- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
背景和以前的搜索
我正在寻找一种优雅的方式来使用 C++14 中基于范围的 for 循环对容器(例如 std::vector)进行反向迭代。寻找我找到的解决方案this Q/A .它基本上告诉我,这不是标准库的一部分,我必须自己使用 boost 或实现一个适配器。我不想使用 boost,所以我现在正在寻找最好的自己的实现。
除了previously mentioned Q/A 中给出的建议,我还找到了this implementation和 this blog关于这个话题。大多数实现都非常相似,看起来相当不错。然而,它们都有一个陷阱:正如 this comment 中指出的那样。如果您使用临时对象调用反向适配器,您最终可能会得到一个悬空引用:
for (const auto& v : reverse_iterate(getContainer()))
关于基于范围的for循环中临时对象的问题,this answer真的帮助了我的理解。但是我们可以做些什么来防止悬空引用呢?
我的解决方案
基于这个背景,我正在寻找一种能够摆脱这个陷阱的实现。在下面的实现中,我使用了一个额外的右值引用 rx_
来延长我的输入参数的生命周期,如果 reverse_iterate
是用右值引用调用的。
编辑:不要使用此解决方案。正如已接受的解决方案中指出的那样,这是错误的。
template <typename T>
class reverse_range
{
T &&rx_; // rvalue-reference to prolong livetime of temporary object
T &x_; // reference to container
public:
explicit reverse_range(T &x) : rx_(T{}), x_(x) {}
explicit reverse_range(T &&rx) : rx_(std::move(rx)), x_(rx_) {}
auto begin() const -> decltype(this->x_.rbegin())
{
return x_.rbegin();
}
auto end() const -> decltype(this->x_.rend())
{
return x_.rend();
}
};
template <typename T>
reverse_range<T> reverse_iterate(T &x)
{
return reverse_range<T>(x);
}
template <typename T>
reverse_range<T> reverse_iterate(T &&rx)
{
return reverse_range<T>(std::move(rx));
}
显然,我们在左值构造函数中构造一个未使用的空容器对象会产生一些开销,但我认为这还不错。此外,还可以通过提供两个类 reverse_range_lvalue
和 reverse_range_rvalue
来摆脱这一点,每个类都提供了其中一种参数类型的实现...
问题
上述扩展会解决悬空引用问题还是我错过了什么?
您对我的代码的其他问题有任何提示吗?
在 C++14 或任何其他( future )版本中是否有更好的想法来解决这个问题?
最佳答案
这行不通。终身扩展在(那部分)构造函数中不起作用。 (它在构造函数的主体中起作用,而不是在成员初始化器列表中)。
template<class R>
struct backwards_t {
R r;
constexpr auto begin() const { using std::rbegin; return rbegin(r); }
constexpr auto begin() { using std::rbegin; return rbegin(r); }
constexpr auto end() const { using std::rend; return rend(r); }
constexpr auto end() { using std::rend; return rend(r); }
};
// Do NOT, I repeat do NOT change this to `backwards_t<std::decay_t<R>>.
// This code is using forwarding references in a clever way.
template<class R>
constexpr backwards_t<R> backwards( R&& r ) { return {std::forward<R>(r)}; }
当传递一个右值时它会移动,当传递一个左值时会保持一个引用。
诀窍在于,对于转发引用 T&&
,如果 T&&
是左值,则 T
是引用,如果 T&&
是一个右值,然后 T
是一个值。因此,我们将左值转换为引用(并且不要复制),同时将右值转换为值(并将右值移动到所述值中)。
for (const auto& v : backwards(getContainer()))
这样就行了。
在 c++17你可以做得“更好”一点;如果您进行聚合初始化,引用生命周期扩展可以应用于结构的内容。但我建议不要这样做;引用生命周期延长在中断时是脆弱和危险的。
c++20 中有谈话或稍后允许编译器将移动到过期对象转换为省略。但我不会打赌它在特定情况下会起作用。我还认为我看到了一篇关于使用生命周期依赖信息标记 ctor 和函数(即返回值取决于参数的生命周期)、允许警告/错误以及可能更通用的生命周期扩展的论文。
所以这是一个已知问题。但这是当今解决此问题的最佳一般安全方法。
关于c++ - 避免基于反向范围的 for 循环实现的悬空引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52722405/
我遵循了 V. Romeo 关于实体管理的教程(在 GitHub 和 Youtube 上)。 然后我尝试重写类 CEntity、CComponent 和测试 CPosition(主要来自 Romeo
为什么下面的代码中有悬空引用?我认为对 const 的引用总是将临时对象的生命周期延长到它们的范围。 boost::filesystem::recursive_directory_iterator i
我有一个多线程应用程序,将传入消息发布到 rabbitmq 交换。使用 rabbitmq java 客户端,我在应用程序启动时创建了一个 rabbitmq 连接,并在我的所有线程中共享它。每个线程都会
我有一个 Git 存储库,其中有大量提交不在特定分支下,我可以 git show 它们,但是当我尝试列出包含它们的分支时,它没有返回任何内容。 我认为这是悬空提交/树问题(由于 -D 分支),所以我修
我是 Linux 的新手,并且继承了保持我们的单一 Linux 服务器运行的职责。这是我们的SVN服务器,所以比较重要。 原来在我之前维护它的人有一个 cron 任务,当有太多 svnserve 进程
我是一名优秀的程序员,十分优秀!