gpt4 book ai didi

c++ - 避免基于反向范围的 for 循环实现的悬空引用

转载 作者:IT老高 更新时间:2023-10-28 22:38:48 26 4
gpt4 key购买 nike

背景和以前的搜索

我正在寻找一种优雅的方式来使用 C++14 中基于范围的 for 循环对容器(例如 std::vector)进行反向迭代。寻找我找到的解决方案this Q/A .它基本上告诉我,这不是标准库的一部分,我必须自己使用 boost 或实现一个适配器。我不想使用 boost,所以我现在正在寻找最好的自己的实现。

除了previously mentioned Q/A 中给出的建议,我还找到了this implementationthis 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_lvaluereverse_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()))

这样就行了。

你可以做得“更好”一点;如果您进行聚合初始化,引用生命周期扩展可以应用于结构的内容。但我建议不要这样做;引用生命周期延长在中断时是脆弱和危险的。

中有谈话或稍后允许编译器将移动到过期对象转换为省略。但我不会打赌它在特定情况下会起作用。我还认为我看到了一篇关于使用生命周期依赖信息标记 ctor 和函数(即返回值取决于参数的生命周期)、允许警告/错误以及可能更通用的生命周期扩展的论文。

所以这是一个已知问题。但这是当今解决此问题的最佳一般安全方法。

关于c++ - 避免基于反向范围的 for 循环实现的悬空引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52722405/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com