避免在基于反向范围的for循环实现中悬挂参考
Avoid dangling reference for reverse range-based for-loop implementation
背景和以前的搜索
我正在寻找一种优雅的方式来使用 C++14 中基于范围的 for 循环对容器(例如 std::vector(进行反向迭代。在寻找解决方案时,我找到了这个问答。它基本上告诉我,这不是标准库的一部分,我必须自己使用 boost 或实现适配器。我不想使用 boost,所以我现在正在寻找最好的自己的实现。
除了前面提到的问答中给出的建议外,我还发现了这个实现和这个关于这个主题的博客。大多数实现都非常相似,看起来相当不错。但是,它们都有一个陷阱:正如此评论中所指出的,如果使用临时对象调用反向适配器,则最终可能会得到一个悬而未决的引用:
for (const auto& v : reverse_iterate(getContainer()))
关于基于范围的for循环中临时对象的问题,这个答案确实有助于我的理解。但是我们可以做些什么来防止悬而未决的引用呢?
我的解决方案
基于这个背景,我正在寻找一种摆脱这个陷阱的实现。在下面的实现中,我使用了一个额外的右值引用rx_
来延长我的输入参数的生存期,reverse_iterate
iff 是用右值引用调用的。
编辑:不要使用此解决方案。正如公认的解决方案所指出的那样,这是错误的。
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));
}
显然,我们在 lvalue 构造函数中构造未使用的空容器对象会产生一些开销,但我认为这还不错。此外,可以通过提供两个类reverse_range_lvalue
和reverse_range_rvalue
来摆脱这种情况,每个类都为其中一个参数类型提供实现......
问题
上面的扩展会解决悬空的引用问题还是我错过了什么?
您对有关我的代码的进一步问题有任何提示吗?
在 C++14 或任何其他(未来(版本中是否有更好的想法来解决此问题?
这行不通。 生存期延长在(该部分(构造函数中不起作用。 (它在构造函数的主体中工作,只是不在成员初始值设定项列表中(。
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++ 中实现嵌套循环的更短方法吗?
- 如何在C++中实现带有packaged_task的异步等待循环?
- 基于范围的变换以实现正常循环
- 实现基于链表的堆栈的基于范围的 for 循环
- 避免在基于反向范围的for循环实现中悬挂参考
- C++ 模板使用递归实现循环
- 是否有可能实现O(N)时间和O(1)空间解决方案,以实现C++中的字符串循环移位
- 如何实现阻塞处理循环?
- 实现循环阵列队列
- 为什么Visual Studio Compiler不在我的Mersenne-Twister实现中循环展开?
- 如何在不停滞主循环的情况下实现对话系统?
- 在 CImg 中实现动画循环的正确方法
- 如何将 A 实现到 B 中,将 B 实现到 A 中(不创建循环)?
- 如何在不使用函数或类的情况下重复代码段,以便在C++中实现高性能循环
- 如何实现一个奇怪的循环
- 如何准确测量和比较opencl速度以实现循环功能的简单速度
- 如何实现循环以增加数据
- 我将如何实现循环调度模拟器
- 使用 deque 在C++中实现循环缓冲区
- 用openmp实现循环并行化的嵌套c++