c++ - 当类有常量时将对象插入向量
问题描述
假设我有一个只有常量的类,因为值永远不会改变。
struct Test {
const std::string id;
const int a;
const double b;
};
稍后,我想将对象添加到向量中,但我希望向量按b
从大到小排序。我使用插入排序,因为只会有一个很小的数字(可能是 5 个)。
std::vector<Test> values;
void addValue( const std::string &id, int a, double b ) {
// Keep stockpiles sorted by weight (large to small)
auto itr = values.begin();
while ( itr != values.end() && itr->b > b ) ++itr;
values.insert( itr, { id, a, b } );
// end sort
}
尝试上述操作时,我在尝试插入向量时收到以下错误:
错误:无法分配“Test”类型的对象,因为它的复制赋值运算符被隐式删除
我想继续使用 avector
来解决这个问题;但我似乎找不到解决排序问题的方法。我能想到的唯一其他选择是有效地vector
不断地重新创建。或者,使用 amulti-set
或类似的,然后一旦添加了所有值,我就可以转储到一个数组中。
有没有办法绕过这个限制,仍然使用 avector
而不是让所有东西都变成非常量?还是我会被迫改变我的结构,或者先搬进一个临时对象?
编辑:还试图避免使用指针
解决方案
正如评论中已经指出的那样,排序需要在向量中的元素周围移动。并且在元素周围移动通常需要移动分配,这通常需要元素的变异......
有一种方法可以摆脱这种困境:与其为现有对象分配一个新值,不如在现有对象之上创建一个新值。可以通过定义一个复制/移动构造函数来做到这一点,例如:
Test& operator =(Test&& other)
{
this->~Test();
return *new (this) Test(std::move(other));
}
这样做只有一个问题:通过[basic.life]/8.3,我们不允许使用任何现有的指针或对原始对象的引用,甚至在这样的赋值之后也不能使用原始对象的名称。您将始终必须使用分配的结果(placement-new 的返回值)或清洗过的指针作为访问对象的唯一方法。由于没有具体说明如何std::sort
操作,我们不能依赖它来做。
我们能做的是构建一个这样的包装器(为了便于阅读,省略了 noexcept):
template <typename T>
class const_element
{
T value;
const T& laundered_value() const { return *std::launder(&value); }
T& laundered_value() { return *std::launder(&value); }
public:
template <typename... Args>
explicit const_element(Args&&... args) : value { std::forward<Args>(args)... } {}
const_element(const const_element& e) : value(e.laundered_value()) {}
const_element(const_element&& e) : value(std::move(e.laundered_value())) {}
const_element& operator =(const const_element& e)
{
laundered_value().~T();
new (&value) T(e.laundered_value());
return *this;
}
const_element& operator =(const_element&& e)
{
laundered_value().~T();
new (&value) T(std::move(e.laundered_value()));
return *this;
}
~const_element()
{
laundered_value().~T();
}
operator const T&() const { return laundered_value(); }
operator T&() { return laundered_value(); }
friend bool operator <(const_element& a, const_element& b)
{
return a.laundered_value() < b.laundered_value();
}
};
这样做是包装某种类型的对象,T
以上述方式实现复制和移动赋值,并确保对当前值的任何访问始终通过清洗过的指针。
然后我们可以做
std::vector<const_element<Test>> values;
values.emplace_back("b", 1, 0.0);
values.emplace_back("a", 0, 0.0);
values.emplace_back("c", 2, 0.0);
std::sort(begin(values), end(values));
这里的工作示例
话虽如此,我建议不要这样做。如果您想要一个无法修改的对象,只需使用 aconst T
而不是T
仅由 const 成员组成的 a。你不能有 的向量const T
。但是你可以有一个向量,T
然后只传递一个对 const 向量或一系列 const 元素的引用……
推荐阅读
- android - Flowable 未从 Room DB 返回值
- mysql - 连接到 AWS 生产数据库的问题
- laravel - 如何从部分表中获取用户?拉拉维尔
- python - 如何从 pandas 数据框中选择平均值大于某个限制的列?
- python - 找到一种方法来有效地执行 DataFrame 的某些列的计算
- javascript - JavaScript 动态创建的隐藏输入不会出现在参数包中
- r - 使用 ggplot2 结合 facet_wrap 和 95% 的密度图面积
- arrays - 将我的帖子更改为 Flutter 中的 JSON 数组
- ios - 如何正确使用 NSCache?
- sql - where 子句 oracle 中的 case 语句