Allow the const member while supporting the = operator in the class

I have several members in my class that are const and therefore can only be initialized through a list of initializers, for example:

 class MyItemT { public: MyItemT(const MyPacketT& aMyPacket, const MyInfoT& aMyInfo) : mMyPacket(aMyPacket), mMyInfo(aMyInfo) { } private: const MyPacketT mMyPacket; const MyInfoT mMyInfo; }; 

My class can be used in some of our inner container classes (like vectors), and these containers require operator= be defined in the class.

Of course, my operator= should do something like this:

 MyItemT& MyItemT::operator=(const MyItemT& other) { mMyPacket = other.mPacket; mMyInfo = other.mMyInfo; return *this; } 

which of course does not work, because mMyPacket and mMyInfo are members of const .

Besides the fact that these members are not const (which I do not want to do), any ideas on how I can fix this?

+6
c ++ operators const
source share
5 answers

You seem to violate the definition of const, if you have an assignment operator that can change them after the construction is completed. If you really need to, I think that Potatoswatter's placement of the new method is probably best, but if you have an assignment operator, your variables are not really constants, as someone can just create a new instance and use it to change their meanings

+7
source share

Instead of storing objects in your containers directly, you can store pointers (or smart pointers). Thus, you do not need to mutate any of the members of your class - you return exactly the same object as you, const and all.

Of course, this is likely to change the memory management of your application, which may well be enough reason not to want to.

+3
source share

This is a dirty hack, but you can destroy and restore yourself:

 MyItemT& MyItemT::operator=(const MyItemT& other) { if ( this == &other ) return *this; // "suggested" by Herb Sutter ;v) this->MyItemT::~MyItemT(); try { new( this ) MyItemT( other ); } catch ( ... ) { new( this ) MyItemT(); // nothrow throw; } return *this; } 

Edit: so that I do not destroy my authority, I really do not do it myself, I would delete const . However, I discussed changing practices because const simply useful and better used where possible.

Sometimes there is a difference between the resource and the value represented by the object. A member can be const through changes to the value as long as the resource is the same, and it would be nice to get security at compile time.

Edit 2: @Charles Bailey provided this wonderful (and very critical) link: http://gotw.ca/gotw/023.htm .

  • Semantics are complex in any derived class operator= .
  • It can be inefficient because it does not invoke specific assignment operators.
  • This is incompatible with overloaded wiki operator& (independently)
  • and etc.

Edit 3: Reflecting on the distinction of “what resource” and “what value”, it seems obvious that operator= should always change the value, not the resource. The resource identifier may be const . In this example, all members are const . If “information” is what is stored inside a “package”, then perhaps the package should be const , but the information is missing.

Thus, the problem is not so much in defining the semantics of assignment, but in the absence of an obvious meaning in this example, if “information” is actually metadata. If any class that owns MyItemT wants to switch it from one package to another, it needs to either refuse, or use auto_ptr<MyItemT> , or refer to a similar hack, as described above (identity verification is not necessary, but catch rest) implemented from the outside. But operator= should not change the resource binding, except as a special feature that absolutely will not interfere with anything else.

Note that this convention works well with Sutter's advice to implement the copy construct in terms of purpose.

  MyItemT::MyItemT( MyItemT const &in ) : mMyPacket( in.mMyPacket ) // initialize resource, const member { *this = in; } // assign value, non-const, via sole assignment method 
+2
source share

You might think that the MyPacketT and MyInfoT should be pointers to const (or smart pointers to const ). Thus, the data itself is still marked as const and is immutable, but you can clean the "exchange" for another const data set in the task, if that makes sense. In fact, you can use the swap icon to complete the assignment in a safe manner.

This way you get the advantage of const to help you prevent accidental resolution of the changes you want to prevent, but you still allow the whole object to be assigned from another object. For example, this allows you to use objects of this class in STL containers.

You can consider this as a special pimpl idiom application.

Something along the lines of:

 #include <algorithm> // for std::swap #include "boost/scoped_ptr.hpp" using namespace boost; class MyPacketT {}; class MyInfoT {}; class MyItemT { public: MyItemT(const MyPacketT& aMyPacket, const MyInfoT& aMyInfo) : pMyPacket(new MyPacketT( aMyPacket)), pMyInfo(new MyInfoT( aMyInfo)) { } MyItemT( MyItemT const& other) : pMyPacket(new MyPacketT( *(other.pMyPacket))), pMyInfo(new MyInfoT( *(other.pMyInfo))) { } void swap( MyItemT& other) { pMyPacket.swap( other.pMyPacket); pMyInfo.swap( other.pMyInfo); } MyItemT const& operator=( MyItemT const& rhs) { MyItemT tmp( rhs); swap( tmp); return *this; } private: scoped_ptr<MyPacketT const> pMyPacket; scoped_ptr<MyInfoT const> pMyInfo; }; 

Finally, I changed my example to use scoped_ptr<> instead of shared_ptr<> because I thought it was a more general idea of ​​what was intended for the OP. However, if the members of the 'reassignable' const can be split (and this is probably true, given my understanding of why the OP wants their const ), then there may be an optimization using shared_ptr<> , and let the copy and assignment operations of the shared_ptr<> class things will be taken care of for these objects - if you don't have other members who need a special copy or assignment of semantics, then your class just got a lot easier, and you can even save a significant part of memory usage by exchanging copies of MyPacketT and MyInfoT .

+1
source share

I think you could leave with a special const proxy.

 template <class T> class Const { public: // Optimal way of returning, by value for built-in and by const& for user types typedef boost::call_traits<T>::const_reference const_reference; typedef boost::call_traits<T>::param_type param_type; Const(): mData() {} Const(param_type data): mData(data) {} Const(const Const& rhs): mData(rhs.mData) {} operator const_reference() const { return mData; } void reset(param_type data) { mData = data; } // explicit private: Const& operator=(const Const&); // deactivated T mData; }; 

Now instead of const MyPacketT you will have Const<MyPacketT> . Not that the interface provides only one way to change it: through an explicit call to reset .

I think that any use of mMyPacket.reset can be easily found. As @MSalters said he protects against Murphy, not Machiavelli :)

+1
source share

All Articles