std::vector of objects and const-correctness

孤者浪人 提交于 2019-12-31 12:50:58

问题


Consider the following:

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

Obviously, the copy constructor is not enough. What am I missing?

EDIT: Ofc. I cannot change this->c in operator=() method, so I don't see how operator=() would be used (although required by std::vector).


回答1:


I'm not sure why nobody said it, but the correct answer is to drop the const, or store A*'s in the vector (using the appropriate smart pointer).

You can give your class terrible semantics by having "copy" invoke UB or doing nothing (and therefore not being a copy), but why all this trouble dancing around UB and bad code? What do you get by making that const? (Hint: Nothing.) Your problem is conceptual: If a class has a const member, the class is const. Objects that are const, fundamentally, cannot be assigned.

Just make it a non-const private, and expose its value immutably. To users, this is equivalent, const-wise. It allows the implicitly generated functions to work just fine.




回答2:


An STL container element must be copy-constructible and assignable1(which your class A isn't). You need to overload operator =.

1 : §23.1 says The type of objects stored in these components must meet the requirements of CopyConstructible types (20.1.3), and the additional requirements of Assignabletypes


EDIT :

Disclaimer: I am not sure whether the following piece of code is 100% safe. If it invokes UB or something please let me know.

A& operator=(const A& assign)
{
    *const_cast<int*> (&c)= assign.c;
    return *this;
}

EDIT 2

I think the above code snippet invokes Undefined Behaviour because trying to cast away the const-ness of a const qualified variable invokes UB.




回答3:


You're missing an assignment operator (or copy assignment operator), one of the big three.




回答4:


The stored type must meet the CopyConstructible and Assignable requirements, which means that operator= is needed too.




回答5:


Probably the assignment operator. The compiler normally generates a default one for you, but that feature is disabled since your class has non-trivial copy semantics.




回答6:


I think the STL implementation of vector functions you are using require an assignment operator (refer Prasoon's quote from the Standard). However as per the quote below, since the assignment operator in your code is implicitly defined (since it is not defined explicitly), your program is ill-formed due to the fact that your class also has a const non static data member.

C++03

$12.8/12 - "An implicitly-declared copy assignment operator is implicitly defined when an object of its class type is assigned a value of its class type or a value of a class type derived from its class type. A program is illformed if the class for which a copy assignment operator is implicitly defined has:

— a nonstatic data member of const type, or

— a nonstatic data member of reference type, or

— a nonstatic data member of class type (or array thereof) with an inaccessible copy assignment operator, or

— a base class with an inaccessible copy assignment operator.




回答7:


Workaround without const_cast.

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



回答8:


I recently ran into the same situation and I used a std::set instead, because its mechanism for adding an element (insert) does not require the = operator (uses the < operator), unlike vector's mechanism (push_back).

If performance is a problem you may try unordered_set or something else similar.




回答9:


You also need to implement a copy constructor, which will look like this:

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

};

The special const_cast template takes a pointer type and casts it back to a writeable form, for occasions such as this.

It should be noted that const_cast is not always safe to use, see here.



来源:https://stackoverflow.com/questions/4124271/stdvector-of-objects-and-const-correctness

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!