请考虑以下事项:
class A {
public:
const int c; // must not be modified!
A(int _c)
: c(_c)
{
// Nothing here
}
A(const A& copy)
: c(copy.c)
{
// Nothing here
}
};
int main(int argc, char *argv[])
{
A foo(1337);
vector<A> vec;
vec.push_back(foo); // <-- compile error!
return 0;
}
显然,复制构造函数还不够。我错过了什么?
编辑: OFC。我无法在operator =()方法中更改this-&gt; c,因此我没有看到如何使用operator =()(尽管std :: vector需要)。
答案 0 :(得分:17)
我不确定为什么没有人这样说,但正确的答案是放弃const
,或将A*
存储在向量中(使用适当的智能指针)。
你可以通过“复制”调用UB或什么也不做(因此不是副本)给你的类带来可怕的语义,但是为什么所有这些麻烦在UB和坏代码中跳舞呢?通过制作const
你会得到什么? (提示:没什么。)你的问题是概念性的:如果一个类有一个const成员,那么这个类就是const。从根本上说,不能分配const的对象。
只需将其设为非const 私有,并以不可变的方式公开其值。对于用户来说,这是等效的,不变的。它允许隐式生成的函数正常工作。
答案 1 :(得分:15)
STL容器元素必须可复制构造且可分配 1 (您的类A
不是)。您需要重载operator =
。
<子> 1
:§23.1
说The type of objects stored in these components must meet the requirements of CopyConstructible
types (20.1.3), and the additional requirements of Assignabletypes
编辑:
免责声明:我不确定以下代码是否100%安全。如果它调用UB或其他什么,请告诉我。
A& operator=(const A& assign)
{
*const_cast<int*> (&c)= assign.c;
return *this;
}
编辑2
我认为上面的代码片段会调用Undefined Behavior,因为试图抛弃const
限定变量的常量会调用UB
。
答案 2 :(得分:7)
您缺少一个赋值运算符(或复制赋值运算符),the big three之一。
答案 3 :(得分:2)
存储的类型必须符合CopyConstructible 和Assignable 要求,这意味着还需要operator =。
答案 4 :(得分:1)
可能是assignment operator。编译器通常会为您生成一个默认值,但由于您的类具有非平凡的复制语义,因此该功能被禁用。
答案 5 :(得分:0)
我认为您使用的向量函数的STL实现需要赋值运算符(请参阅Prasoon引用的标准)。但是根据下面的引用,由于代码中的赋值运算符是隐式定义的(因为它没有明确定义),所以由于你的类也有一个非静态数据成员,你的程序是不正确的。
C ++ 03
$ 12.8 / 12 - “隐含声明 复制赋值运算符是隐式的 在其类的对象时定义 type被赋予其类的值 类型或类类型的值 派生自其类类型。一个程序 如果是哪一类,那就是不正确的 复制赋值运算符是隐式的 定义有:
- const类型的非静态数据成员,或
- 非静态数据 引用类型的成员,或
- a 类类型的非静态数据成员 (或其数组)带有 无法访问的副本分配操作员, 或
- 一个无法访问的基类 复制赋值运算符。
答案 6 :(得分:0)
您还需要实现一个复制构造函数,如下所示:
class A {
public:
const int c; // must not be modified!
A(int _c)
...
A(const A& copy)
...
A& operator=(const A& rhs)
{
int * p_writable_c = const_cast<int *>(&c);
*p_writable_c = rhs.c;
return *this;
}
};
特殊const_cast
模板采用指针类型并将其强制转换为可写形式,适用于此类情况。
应该注意const_cast
使用并不总是安全的,请参阅here。
答案 7 :(得分:0)
没有const_cast
的解决方法。
A& operator=(const A& right)
{
if (this == &right) return *this;
this->~A();
new (this) A(right);
return *this;
}
答案 8 :(得分:0)
我最近碰到了相同的情况而且我使用了std :: set,因为它添加元素(insert)的机制不需要=运算符(使用&lt;运算符),这与vector&#39; s不同。机制(push_back)。
如果性能有问题,您可以尝试使用unordered_set或类似的其他内容。
答案 9 :(得分:0)
我只想指出,从C ++ 11及更高版本开始,问题中的原始代码可以正常编译!完全没有错误。但是,vec.emplace_back()
会是更好的调用,因为它在内部使用“ placement new”,因此效率更高,它可以在向量的末尾将对象直接复制构造到内存中,而不是额外的中间副本
cppreference states(强调):
std::vector<T,Allocator>::emplace_back
在容器的末尾添加一个新元素。元素是通过
std::allocator_traits::construct
构造的,该元素通常使用 placement-new 在容器提供的位置就地构造元素。
下面是一个快速演示,显示vec.push_back()
和vec.emplace_back()
现在都可以正常工作。
在此处运行:https://onlinegdb.com/BkFkja6ED。
#include <cstdio>
#include <vector>
class A {
public:
const int c; // must not be modified!
A(int _c)
: c(_c)
{
// Nothing here
}
// Copy constructor
A(const A& copy)
: c(copy.c)
{
// Nothing here
}
};
int main(int argc, char *argv[])
{
A foo(1337);
A foo2(999);
std::vector<A> vec;
vec.push_back(foo); // works!
vec.emplace_back(foo2); // also works!
for (size_t i = 0; i < vec.size(); i++)
{
printf("vec[%lu].c = %i\n", i, vec[i].c);
}
return 0;
}
输出:
vec[0].c = 1337
vec[1].c = 999