std::erase() 非引用与引用的行为
Behavior with std::erase() non-references vs reference
假设我有一个大小为 3 的 my_object
类型的向量,我想从我的向量中获取 3 个元素,将它们存储在一个引用中
然后我想使用 std::remove_if()
和 element_1 删除和擦除 element_3,使用 std::remove
删除和删除 element_2
这里是my_object
:
class my_object {
public:
my_object(int num);
bool exists() const;
private:
int num;
};
my_object::my_object(int num) : num(num) {}
bool my_object::exists() { return num == 1; }
这里是main
:
std::vector<my_object> my_vector;
int main() {
my_object e1(2);
my_object e2(2);
my_object e3(1); // i.e exists() will return true in lambda
my_vector.push_back(e1);
my_vector.push_back(e2);
my_vector.push_back(e3);
const auto& element_1 = my_vector.at(0);
const auto& element_2 = my_vector.at(1);
const auto& element_3 = my_vector.at(2);
auto lambda = [](auto& src) { return src.exists() };
std::erase(std::remove_if(b, e, lambda), e); // remove_if for element_3
std::erase(std::remove(b, e, element_1), e);
std::erase(std::remove(b, e, element_2), e);
return 0;
}
非常奇怪的是,当我通过引用声明 element_1、element_2、element_3 时,擦除没有正确完成,大小也没有减小到 0 ,但是当我写 const auto
时没有 &
然后它工作得很好,任何人都可以向我解释这种奇怪的行为吗?
不考虑擦除方法,这些引用只是:对对象生活在容器中的引用。一旦 remove
或 remove_if
在序列中向上移动时执行了他们的任务移动分配,这些引用仍然指的是相同的元素,但这些位置的占用者是:
- 充其量仍然是有效对象,因为有效对象要么留在原处,要么被移动分配到那里。
- 只是一些以前的自我的外壳,因为引用现在指的是一个从未被目标移动回收的源对象。
我不打算深入 std::remove
,而是。看看 std::remove_if
这个相当简单的例子
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> v = { 1,2,3,4,5 };
const auto& a1 = v.at(0);
const auto& a2 = v.at(2);
const auto& a3 = v.at(4);
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
std::remove_if(v.begin(), v.end(), [](const auto& x) { return x == 3; });
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
}
输出
1 3 5
1 4 5
如您所见,std::remove_if
的功能描述与您在代码中看到的一致。 3
元素被删除,4
元素被移动分配到它的位置。您在这里看不到的是 5
元素被移动分配到 4
的位置,而您现在在这里看到的 5
值恰好来自插槽其中 5
为 。该标准表示该对象是 "valid",但具有 "unspecified" 值。我们可以通过确保我们的移动分配的移动源实际上是 "invalid"(就我们而言)来验证这一点。修改我们的原始程序给我们这个:
#include <iostream>
#include <vector>
#include <algorithm>
struct S
{
S(int n) : value(n), isvalid(true)
{
}
S(const S& s) : value(s.value), isvalid(true)
{
}
S(S&& s) : value(s.value), isvalid(true)
{
s.isvalid = false;
}
S& operator =(S&& s)
{
value = s.value;
isvalid = s.isvalid;
s.isvalid = false;
return *this;
}
int value;
bool isvalid;
};
std::ostream& operator <<(std::ostream& outp, const S& s)
{
outp << s.value << '(' << std::boolalpha << s.isvalid << ')';
return outp;
}
int main()
{
std::vector<S> v = { 1,2,3,4,5 };
const auto& a1 = v.at(0);
const auto& a2 = v.at(2);
const auto& a3 = v.at(4);
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
std::remove_if(v.begin(), v.end(), [](const auto& x) { return x.value == 3; });
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
}
输出
1(true) 3(true) 5(true)
1(true) 4(true) 5(false)
底线:您的引用仍然指的是与之前相同的插槽,但元素已 (a) 移动分配给其他内容,或 (a) 不再包含指定内容。在执行容器修改时使用对容器内容的引用时要小心。
我保留对 std::erase
电话的评论,因为我根本不知道你在那里做什么。据我所知,这甚至不是标准库中的一个函数(这不是我第一次错过一个新函数,但是 cppreference 没有产生任何结果,所以请按它的价值去做)。
假设我有一个大小为 3 的 my_object
类型的向量,我想从我的向量中获取 3 个元素,将它们存储在一个引用中
然后我想使用 std::remove_if()
和 element_1 删除和擦除 element_3,使用 std::remove
这里是my_object
:
class my_object {
public:
my_object(int num);
bool exists() const;
private:
int num;
};
my_object::my_object(int num) : num(num) {}
bool my_object::exists() { return num == 1; }
这里是main
:
std::vector<my_object> my_vector;
int main() {
my_object e1(2);
my_object e2(2);
my_object e3(1); // i.e exists() will return true in lambda
my_vector.push_back(e1);
my_vector.push_back(e2);
my_vector.push_back(e3);
const auto& element_1 = my_vector.at(0);
const auto& element_2 = my_vector.at(1);
const auto& element_3 = my_vector.at(2);
auto lambda = [](auto& src) { return src.exists() };
std::erase(std::remove_if(b, e, lambda), e); // remove_if for element_3
std::erase(std::remove(b, e, element_1), e);
std::erase(std::remove(b, e, element_2), e);
return 0;
}
非常奇怪的是,当我通过引用声明 element_1、element_2、element_3 时,擦除没有正确完成,大小也没有减小到 0 ,但是当我写 const auto
时没有 &
然后它工作得很好,任何人都可以向我解释这种奇怪的行为吗?
不考虑擦除方法,这些引用只是:对对象生活在容器中的引用。一旦 remove
或 remove_if
在序列中向上移动时执行了他们的任务移动分配,这些引用仍然指的是相同的元素,但这些位置的占用者是:
- 充其量仍然是有效对象,因为有效对象要么留在原处,要么被移动分配到那里。
- 只是一些以前的自我的外壳,因为引用现在指的是一个从未被目标移动回收的源对象。
我不打算深入 std::remove
,而是。看看 std::remove_if
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> v = { 1,2,3,4,5 };
const auto& a1 = v.at(0);
const auto& a2 = v.at(2);
const auto& a3 = v.at(4);
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
std::remove_if(v.begin(), v.end(), [](const auto& x) { return x == 3; });
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
}
输出
1 3 5
1 4 5
如您所见,std::remove_if
的功能描述与您在代码中看到的一致。 3
元素被删除,4
元素被移动分配到它的位置。您在这里看不到的是 5
元素被移动分配到 4
的位置,而您现在在这里看到的 5
值恰好来自插槽其中 5
为 。该标准表示该对象是 "valid",但具有 "unspecified" 值。我们可以通过确保我们的移动分配的移动源实际上是 "invalid"(就我们而言)来验证这一点。修改我们的原始程序给我们这个:
#include <iostream>
#include <vector>
#include <algorithm>
struct S
{
S(int n) : value(n), isvalid(true)
{
}
S(const S& s) : value(s.value), isvalid(true)
{
}
S(S&& s) : value(s.value), isvalid(true)
{
s.isvalid = false;
}
S& operator =(S&& s)
{
value = s.value;
isvalid = s.isvalid;
s.isvalid = false;
return *this;
}
int value;
bool isvalid;
};
std::ostream& operator <<(std::ostream& outp, const S& s)
{
outp << s.value << '(' << std::boolalpha << s.isvalid << ')';
return outp;
}
int main()
{
std::vector<S> v = { 1,2,3,4,5 };
const auto& a1 = v.at(0);
const auto& a2 = v.at(2);
const auto& a3 = v.at(4);
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
std::remove_if(v.begin(), v.end(), [](const auto& x) { return x.value == 3; });
std::cout << a1 << ' ' << a2 << ' ' << a3 << '\n';
}
输出
1(true) 3(true) 5(true)
1(true) 4(true) 5(false)
底线:您的引用仍然指的是与之前相同的插槽,但元素已 (a) 移动分配给其他内容,或 (a) 不再包含指定内容。在执行容器修改时使用对容器内容的引用时要小心。
我保留对 std::erase
电话的评论,因为我根本不知道你在那里做什么。据我所知,这甚至不是标准库中的一个函数(这不是我第一次错过一个新函数,但是 cppreference 没有产生任何结果,所以请按它的价值去做)。