【发布时间】:2012-10-06 04:20:38
【问题描述】:
关于替换不可赋值的向量元素存在两个问题:
一个对象不可赋值的一个典型原因是它的类定义包括const成员,因此它的operator=被删除了。
std::vector 要求其元素类型是可分配的。事实上,至少使用 GCC,当对象不可赋值时,直接赋值 (vec[i] = x;) 或 erase() 和 insert() 的组合替换元素都不起作用。
可以使用像下面这样使用vector::data()、直接元素销毁和使用复制构造函数放置新的函数来替换元素而不会导致未定义的行为吗?
template <typename T>
inline void replace(std::vector<T> &vec, const size_t pos, const T& src)
{
T *p = vec.data() + pos;
p->~T();
new (p) T(src);
}
下面是一个正在使用的函数的例子。这在 GCC 4.7 中编译并且似乎可以工作。
struct A
{
const int _i;
A(const int &i):_i(i) {}
};
int main() {
std::vector<A> vec;
A c1(1);
A c2(2);
vec.push_back(c1);
std::cout << vec[0]._i << std::endl;
/* To replace the element in the vector
we cannot use this: */
//vec[0] = c2;
/* Nor this: */
//vec.erase(begin(vec));
//vec.insert(begin(vec),c2);
/* But this we can: */
replace(vec,0,c2);
std::cout << vec[0]._i << std::endl;
return 0;
}
【问题讨论】:
-
@jalf 关键是你根本不会调用赋值运算符。
-
我见过的几乎所有 STL 实现(以及现在的标准库)都将这种技术用于序列管理的高天。您在这里使用的这个特定用法中唯一让我大吃一惊的部分是放置构造可能抛出的可能性,此时您现在在向量中有一个被破坏的元素,但向量不知道这一点并且会尝试在自己的毁灭中再次毁灭它。好问题,顺便说一句。
-
@WhozCraig:我同意,也许这个函数的条件是复制构造函数是 nothrow ? (
is_nothrow_copy_constructible) -
因为值语义妨碍了。来自 Java 或其他“面向引用”的编程语言,人们期望能够将常量对象绑定到可变变量。只要在
X类中有一个const成员,就不能再分配给X类型的变量(因为变量只不过是 C++ 中的命名对象),而这很少是人们想要的。 -
@jogojapan 编译器可以并且确实执行此要求。尝试使用
g++ -D_GLIBCXX_CONCEPT_CHECKS进行编译。 (有一次,它的意图是要求它无法编译。由于概念没有进入这个版本,所以推迟了这个,但我希望在标准的下一个版本中会出现这种情况。)
标签: c++ vector c++11 std placement-new