繁体   English   中英

std::vector 对象和常量正确性

[英]std::vector of objects and const-correctness

考虑以下:

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

显然,拷贝构造函数是不够的。 我错过了什么?

编辑:
办公室我无法在operator=()方法中更改this->c ,所以我看不到operator=()将如何使用(尽管std::vector )。

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

通过“复制”调用 UB 或什么都不做(因此不是副本),您可以给您的班级带来糟糕的语义,但是为什么所有这些麻烦都围绕着 UB 和糟糕的代码? 制作那个const会得到什么? (提示:没什么。)你的问题是概念性的:如果一个类有一个 const 成员,这个类就是 const。 本质上,const 的对象不能被赋值。

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

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限定变量的常量性会调用UB

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

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

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

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

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

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

C++03

$12.8/12 - “隐式声明的复制赋值运算符是在为其类类型的对象分配其类类型的值或从其类类型派生的类类型的值时隐式定义的。如果类隐式定义了复制赋值运算符的有:

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

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

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

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

您还需要实现一个复制构造函数,它看起来像这样:

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使用并不总是安全的,请参见 此处

没有const_cast解决方法。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM