c++ - 避免未定义的行为:临时对象
问题描述
我已经编写了一个类,将其用作方便的视图,例如在基于范围的for
s 中。总的来说,它只是一对带有边界检查的迭代器:
template<typename I> class Range {
private:
I begin;
I end;
public:
Range(I const begin, I const end)
: begin(begin), end(end)
{}
Range<I>& skip(int const amount) {
begin = std::min(begin + amount, end);
return *this;
}
};
template<typename C> auto whole(C const& container) {
using Iterator = decltype(std::begin(container));
return Range<Iterator>(std::begin(container), std::end(container));
}
这是它的预期用途(导致 UB):
std::vector<int> const vector{1, 2, 3};
for (int const value : whole(vector).skip(1)) {
std::cout << value << ' ';
}
删除该skip(1)
部分有帮助,与以下重构相同Range::skip
:
Range<I> skip(int const amount) const {
I const new_begin = std::min(begin + amount, end);
return Range<I>(new_begin, end);
}
似乎临时不能返回对自身的引用。这是 cppreference 所说的:
作为评估完整表达式的最后一步,所有临时对象都被销毁,该完整表达式(词法上)包含创建它们的点,如果创建了多个临时对象,它们将按照与创建顺序相反的顺序被销毁。
虽然我不确定情况是否如此,但我现在不知道如何实际解释它。实际问题是什么,我怎样才能可靠地避免这种 UB?类似的表达例如auto string = std::string("abc").append("def")
也不安全吗?
解决方案
在基于范围的 for 循环中,范围表达式必须引用为(原型代码)
auto && __range = whole(vector).skip(1) ;
问题是创建的临时whole(vector)
对象在完整表达式之后立即被销毁,引用__range
(绑定到从返回的引用skip
,即临时对象)变得悬空;之后任何取消引用都会导致 UB。
auto string = std::string("abc").append("def")
很好,string
被复制了,它是一个独立于临时的对象std::string
。
从 C++20 开始,您可以添加init-statement:
如果range_expression返回一个临时值,则它的生命周期会延长到循环结束,如绑定到转发引用__range 所示,但请注意range_expression 内的任何临时值的生命周期都不会延长。
例如
std::vector<int> const vector{1, 2, 3};
for (auto thing = whole(vector); int const value : thing.skip(1)) {
std::cout << value << ' ';
}
推荐阅读
- wildfly - Wildfly 14 Web 控制台在模块选项中添加空格
- python - 使用 Selenium、PhantomJS 和 BS4 进行抓取
- excel - 在 Excel 中搜索值并返回另一个值
- azure - 使用 Azure AD 进行身份验证和授权
- node.js - Azure 微软机器人框架。未找到部署(vs 代码)web.config 文件问题
- r - 如何在R中的数据框中插入行数较少的列
- r - R语言,如何通过跳过相同数字的组来求和值(嵌套在另一个组中)?
- java - RX Java 中的分组并行调用
- python - PRAW 如何捕获“收到的 401 HTTP 响应”
- datetime - 颤振飞镖错误将时间戳转换为没有时间的日期