使用const成员分配类

时间:2012-07-22 16:30:42

标签: c++ vector const assignment-operator placement-new

请考虑以下代码:

struct s
{
    const int id;

    s(int _id):
        id(_id)
    {}
};
// ...
vector<s> v;  v.push_back(s(1));

我收到编译错误,'const int id'不能使用默认赋值运算符。

<击> Q1。为什么push_back()需要赋值赋值运算符?
A1。因为当前的c ++标准是这样说的。

<击> Q2。我该怎么办?

  • 我不想放弃const说明符
  • 我希望复制数据

<击> A2。我将使用智能指针。

Q3。我提出了一个“解决方案”,这似乎相当疯狂:

s& operator =(const s& m)
{
    if(this == &m) return *this;
    this->~s();
    return *new(this) s(m);
}

我应该避免这种情况,为什么(如果是这样)? 如果对象在堆栈中,使用placement new是否安全?

7 个答案:

答案 0 :(得分:5)

C ++ 03要求存储在容器中的元素为CopyConstructibleAssignable(参见§23.1)。因此,实现可以决定在他们认为合适时使用复制构造和赋值。在C ++ 11中放松了这些约束。明确地,push_back操作要求是类型为CopyInsertable到向量中(参见§23.2.3序列容器)

此外,C ++ 11容器可以在插入操作中使用移动语义并继续操作。

答案 1 :(得分:3)

s& operator =(const s& m)
{
    if(this == &m) return *this;
    this->~s();
    return *new(this) s(m);
}
     

我应该避免这种情况,为什么(如果是这样)?如果对象在堆栈上,使用placement new是否安全?

如果可以,你应该避免它,不是因为它形成不良,而是因为读者很难理解你的目标和对这段代码的信任。作为程序员,您应该致力于减少您编写的WTF /代码行数。

,这是合法的。根据

  

[new.delete.placement]/3

void* operator new(std::size_t size, void* ptr) noexcept;
     

3备注:故意不执行任何其他操作。

调用placement new不会分配或释放内存,相当于手动调用s的复制构造函数,如果s有一个简单的析构函数,则根据[basic.life]/8是合法的

答案 2 :(得分:2)

  

我不想放弃const说明符

嗯,你别无选择。

s& operator =(const s& m) {
    return *new(this) s(m); 
}

未定义的行为。

有一个原因,为什么几乎没有人使用const成员变量,这是因为这个原因。你无能为力。 const成员变量不能在您想要分配的类型中使用。这些类型是不可变的,就是这样,而vector的实现需要可变性。

答案 3 :(得分:1)

  

Q2。我该怎么办?

存储指针,最好是智能。

vector<unique_ptr<s>> v;
v.emplace_back(new s(1));

答案 4 :(得分:1)

确定,

您应该始终通过简单的步骤来思考问题。

std::vector<typename T>::push_back(args);   

需要在矢量数据中保留空格,然后将参数的值分配(或复制或移动)到该位置的 vector.data()[idx] 的内存中。

要理解为什么你不能在成员函数 std :: vector :: push_back 中使用你的结构,试试这个:

std::vector<const int> v; // the compiler will hate you here, 
                          // because this is considered ill formed.

形成错误的原因是类 std :: vector 的成员函数可以调用其模板参数的赋值运算符,但在这种情况下它是一个常量类型参数&#34; const int &#34;这意味着它没有赋值操作符(分配给const变量是没意义的!!)。 对于具有 const数据成员类类型,会发现相同的行为。因为编译器将删除默认赋值运算符,所以驱逐

struct S
{
    const int _id; // automatically the default assignment operator is 
                   // delete i.e.  S& operator-(const S&) = delete;
};
// ... that's why you cannot do this
std::vector<S> v; 
v.Push_back(S(1234));

但是如果你想保留意图并在一个格式良好的代码中表达它,那么你应该这样做:

class s
{
    int _id;
public:
    explicit s(const int& id) :
    _id(id)
    {};

    const int& get() const
    {
    return _id; 
    }; // no user can modify the member variable after it's initialization

};
// this is called data encapsulation, basic technique!
// ...
std::vector<S> v ; 
v.push_back(S(1234)); // in place construction

如果你想破坏规则并强加一个可分配的常量类类型,那么就按照上面提到的那样做。

答案 5 :(得分:0)

这不是一个真正的解决方案,而是一种解决方法:

#include <vector>
struct s
{
  const int id;
  s(int _id):
    id(_id)
    {}
};

int main(){
  std::vector<s*> v;  
  v.push_back(new s(1));
  return 0;
}

这将存储s的指针而不是对象本身。至少它编译......;)

编辑:您可以使用智能c ++ 11指针增强此功能。见本杰明林德利的答案。

答案 6 :(得分:-2)

在赋值运算符中使用const_cast:

S& operator=(const S& rhs)
{
    if(this==&rhs) return *this;
    int *pid=const_cast<int*>(&this->id);
    *pid=rhs.id;
    return *this;
}