12

考虑以下代码:

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);
}

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

4

7 回答 7

5

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

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

于 2012-07-22T16:49:02.567 回答
4

我不想放弃 const 说明符

好吧,你别无选择。

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

未定义的行为。

几乎没有人使用const成员变量是有原因的,就是因为这个。你对此无能为力。const成员变量根本不能用于您想要分配的类型。这些类型是不可变的,仅此而已,您的实现vector需要可变性。

于 2012-07-22T17:06:15.877 回答
4
s& operator =(const s& m)
{
    if(this == &m) return *this;
    this->~s();
    return *new(this) s(m);
}

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

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

但是,这是合法的。根据

[new.delete.placement]/3

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

3 备注:故意不执行其他动作。

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

于 2017-11-24T14:44:53.197 回答
2

行,

您应该始终以简单的步骤考虑问题。

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类的成员函数可以调用其模板参数的赋值运算符,但在这种情况下,它是一个常量类型参数“ const int ”,这意味着它没有赋值运算符(赋值给 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

如果你想打破规则并强加一个可分配的常量类类型,那么就按照上面的建议去做。

于 2017-11-24T17:35:37.473 回答
1

Q2。我该怎么办?

存储指针,最好是智能的。

vector<unique_ptr<s>> v;
v.emplace_back(new s(1));
于 2012-07-22T17:03:16.010 回答
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 指针来增强它。请参阅本杰明林德利的回答。

于 2012-07-22T17:10:02.243 回答
-3

在赋值运算符中使用 const_cast:

S& operator=(const S& rhs)
{
    if(this==&rhs) return *this;
    int *pid=const_cast<int*>(&this->id);
    *pid=rhs.id;
    return *this;
}
于 2013-02-21T16:05:48.770 回答