17

考虑以下:

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。我无法改变this->c方法operator=(),所以我看不到operator=()将如何使用(尽管 要求std::vector)。

4

10 回答 10

21

我不确定为什么没有人说,但正确的答案是将const, 或 storeA*放在向量中(使用适当的智能指针)。

您可以通过让“复制”调用 UB 或什么都不做(因此不是复制品)给您的班级提供可怕的语义,但是为什么所有这些麻烦都围绕着 UB 和糟糕的代码跳舞呢?这样做你能得到什么const?(提示:什么都没有。)你的问题是概念性的:如果一个类有一个 const 成员,那么这个类就是 const。从根本上说,不能分配 const 的对象。

只需将其设为非常量private,并不可变地公开其值即可。对于用户来说,这是等价的,const-wise。它允许隐式生成的函数正常工作。

于 2010-11-09T19:30:22.340 回答
16

STL 容器元素必须是可复制构造和可赋值的1(您的类A不是)。你需要超载operator =

1:§23.1The 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

我认为上面的代码片段调用了未定义的行为,因为试图抛弃const合格变量的 const-ness 调用UB.

于 2010-11-08T13:37:14.090 回答
7

您缺少一个赋值运算符(或复制赋值运算符),这是三大.

于 2010-11-08T13:36:41.903 回答
2

存储类型必须满足 CopyConstructible和 Assignable要求,这意味着也需要 operator=。

于 2010-11-08T13:36:41.363 回答
1

可能是赋值运算符。编译器通常会为您生成一个默认的,但该功能被禁用,因为您的类具有非平凡的复制语义。

于 2010-11-08T13:36:07.863 回答
0

我最近遇到了同样的情况,我使用了 std::set 代替,因为它添加元素(插入)的机制不需要 = 运算符(使用 < 运算符),这与向量的机制(push_back)不同。

如果性能是一个问题,您可以尝试 unordered_set 或其他类似的东西。

于 2015-03-09T17:46:12.513 回答
0

没有const_cast.

A& operator=(const A& right) 
{ 
    if (this == &right) return *this; 
    this->~A();
    new (this) A(right);
    return *this; 
} 
于 2010-11-08T15:21:40.317 回答
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是,使用起来并不总是安全的,请参见此处

于 2010-11-08T14:29:47.660 回答
0

我认为您正在使用的向量函数的 STL 实现需要一个赋值运算符(请参阅标准中的 Prasoon 引用)。但是,根据下面的引用,由于代码中的赋值运算符是隐式定义的(因为它没有显式定义),因此您的程序格式错误,因为您的类也有一个 const 非静态数据成员。

C++03

$12.8/12 - “一个隐式声明的复制赋值运算符是隐式定义的,当它的类类型的对象被分配一个它的类类型的值或者一个从它的类类型派生的类类型的值时。如果类的程序是错误的隐式定义了复制赋值运算符的有:

— const 类型的非静态数据成员,或

— 引用类型的非静态数据成员,或

— 具有不可访问的复制赋值运算符的类类型(或其数组)的非静态数据成员,或

— 具有不可访问的复制赋值运算符的基类。

于 2010-11-08T13:49:38.477 回答
0

我只想指出,从 C++11 及更高版本开始,问题中的原始代码编译得很好!完全没有错误。然而,vec.emplace_back()这将是一个更好的调用,因为它在内部使用“placement new”,因此更有效,将对象复制到向量末尾的内存中,而不是拥有一个额外的中间副本。

cppreference 状态(强调添加):

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
于 2020-09-15T05:04:06.253 回答