Class A {
public:
A& operator=(const A& rhs);
private:
const int i;
int j;
};
A& A::operator=(const A& rhs) {
j = rhs.j; // this is OK
i = rhs.i; // compiler error! how can I make this work?
}
// Not shown: operator= will check for assignment to self
// and return *this, like all good assignment operators.
Thanks for any help!
Ajay Vijay
April 5th, 2005, 03:39 AM
What's the purpose of assigning const member, in your sense?
You also did not have constructor in class that assigns const data member.
Anyways, you can use C-Style casting or const_casting to do that:A& A::operator=(const A& rhs) {
j = rhs.j; // this is OK
*(const_cast<int*>(&i)) = rhs.i; // compiler error! how can I make this work?
}
cilu
April 5th, 2005, 03:41 AM
Use const_cast operator.
n.marler
April 5th, 2005, 03:49 AM
I need assignment operator so that I can use in STL containers (std::vector for now). But as I'm typing this, I'm thinking that maybe I should just use containers of pointers (A*). That's probably the more efficient way to go, anyway.
You're right, I didn't show my ctors. Those were easier to write, initializing the const member in the member initialization list.
So, I'll try containers of pointers, and use your const_cast suggestions if I need to.
Thanks
marten_range
April 5th, 2005, 04:26 AM
If you really need to have const members then pointers are the way to go as one should always strive to avoid casting.
But raw pointers doesn't work to well with the standard containers and algorithms. What you could do is using a reference counted smart pointer such as boost::shared_ptr (http://www.boost.org/libs/smart_ptr/smart_ptr.htm).
std::vector< boost::shared_ptr<A> > my_vector;
matthias_k
April 5th, 2005, 04:38 AM
This is an interesting question.
Is that really the way to go? Using const_cast<> sounds like the quick and dirty way. It also breaks the class, because you can abuse the operator= to assign to a const member. That doesn't sound like a good idea.
On the other hand, since STL containers are built around object copies, there has to be a cleaner solution, no?
matthias_k
April 5th, 2005, 04:41 AM
Sorry, missed your post. Using pointers sounds like a reasonable solution, though you will run into trouble when using STL algorithms, because they will be invoked on the pointers, not on the pointees. There are several solutions to this of course, but one should keep it in mind.
Now, how about doing it when the class contains a reference variable?
cma
April 5th, 2005, 03:06 PM
Doesn't that cause undefined behavior (using const_cast and then modifing the member)?
n.marler
April 5th, 2005, 04:06 PM
Not sure that I see the difference between Graham's code and the prior suggestion to const_cast; seems they both have the same result. In fact, it might be considered more inefficient because of constructing the extra object "temp".
As I've thought about this more today, I'm resigning myself to the fact that your only opportunity to have control over const and reference members is during initialization. Once the object is constructed and you can call the assignment operator, it's too late to make any changes. Thus, if you want to identical copies of an object you must use the copy ctor. I think this is the only consistent answer to my question about const ints and Graham's question about references.
Of course no assignment operator precludes putting the objects directly into an STL container, but you can always use containers of pointers (as long as you're careful).
Ajay Vijay
April 6th, 2005, 12:27 AM
I also disregard with the post given by Graham. It does not matter if it works in VC7 and not in VC6, but it complicates the problem. All in all, we need to use 'const_cast' operator, whether we use '=' or 'std::swap'.
And yes, why "swapping" at all?? We need to copy from source to destination, not exchanging them..!
wien
April 6th, 2005, 01:48 AM
And yes, why "swapping" at all?? We need to copy from source to destination, not exchanging them..!Exception safety. If you changed j, and then the modification of i threw an exception (it will of course not do that in this example, but might if i was an user-defined type), the object would be in an undefined state. (bad) Graham's version doesn't suffer from that problem.
n.marler
April 6th, 2005, 02:27 AM
I like the idea of exception safety, but sorry, I don't see it in Graham's code. If an exception occurs in foo::swap things could still be left in a bad state.
To implement exception safety, seems there should be a mechanism to catch the exception and undo partial changes.
Gabriel Fleseriu
April 6th, 2005, 03:14 AM
I like the idea of exception safety, but sorry, I don't see it in Graham's code. If an exception occurs in foo::swap things could still be left in a bad state.
To implement exception safety, seems there should be a mechanism to catch the exception and undo partial changes.
The entire idea behind the swap() technique is that swap() doesn't throw. However, using const_cast to modify a const value will yield undefined behaviour, I think.
Semantically, I'd say that objects with const members are not assignable and thus cannot be safely put into standard containers.
darwen
April 6th, 2005, 03:27 AM
You could use the 'mutable' keyword on your const member. It only works for members though.
Darwen.
Graham
April 6th, 2005, 03:44 AM
Good grief. I was playing about trying to make sense of the relevant section in the standard (5.2.11), and I got the process to work, so I thought I'd let people know. I didn't expect the Spanish Inquisition... ;)
And yes, the swap trick is about exception safety. I could have simplified the code, but I routinely write my assignment operators that way, so that's how it got written. If you can't see why it's exception safe, then I suggest you read Exceptional C++ for all the gory details.
Ajay Vijay
April 6th, 2005, 04:29 AM
You could use the 'mutable' keyword on your const member. It only works for members though.Are you sure that you can use 'const' and 'mutable' specifiers on same data-member of class?class See
{
mutable const x;
};e:\ajay....(98) : error C2071: 'x' : illegal storage classe:\Ajay\...(98) : error C2071: 'See::x' : illegal storage class
wien
April 6th, 2005, 04:53 AM
I didn't expect the Spanish Inquisition... ;)Nobody expects the Spanish Inquisition! :D
Gabriel Fleseriu
April 6th, 2005, 05:58 AM
Are you sure that you can use 'const' and 'mutable' specifiers on same data-member of class?
No. Either const or mutable. "Mutable" means that a data member may be changed on a const object -- this is not quite related to the current thread. Suppose you have a polygon class with a const getter GetSurface(). The getter computes the surface and caches it in a data member prior returning it. Now it makes sense to have that data member 'mutable', to allow a 'const polygon' to cache. Semantically the mutable member shall not be part of the visible state of the object.
Ajay Vijay
April 6th, 2005, 06:01 AM
Thanks Gabriel, I am quite aware of that technique. My post was with the text posted by Graham:You could use the 'mutable' keyword on your const member...
marten_range
April 6th, 2005, 06:33 AM
If one starts to add keywords like mutable to the members why not remove the const all together?
The problem I see with this is that the assignment operator breaks the semantics of the const variable.
If you want to support assignment then the member variable shouldn't be const.
Now, you might not have control over the source code and in that case I think const_cast will work fine in most cases (together with a comment why the const_cast is there).
There is a risk with modern compilers that they place const variables in protected memory but I haven't run into that myself yet.
If you want to use const to protect against accidental modification (a noble cause) then I would consider making a template wrapper class that doesn't allow modifcation but do allow swapping and copy construction (with a non-throwing swap and a copy-construction one can as demonstrated above implement assignment)
Then there is the possibility to disallow assignment and use smart pointers.
So in order to be able to offer a better advise I think the OP should provide more background information.
Graham
April 6th, 2005, 08:32 AM
Ajay: I never said that...
n.marler
April 6th, 2005, 08:51 AM
Hi Graham - you're not getting the Spanish Inquisition from me! I appreciate all the input and am happy to have learned the swap construct. I'll add that book to my reading list.
Gabriel Fleseriu
April 6th, 2005, 09:32 AM
I think that Graham tends to "get the Spanish Inquisition" because many people can't wait to "catch" him making a mistake. That, because he very rarely makes any...;)
marten_range
April 6th, 2005, 09:49 AM
n.marier: If you're interested in exception safety the book 'Exceptional C++' (http://www.gotw.ca/publications/xc++.htm) goes into great depths to explain why and how some common techniques (such as implementing assignment as copy-and-swap) works.
Plus much more.
It's an "exceptional" book.
matthias_k
April 6th, 2005, 10:07 AM
Not that anyone cares, but terrashop.de had it in pdf-format for 2.50€, in their ebook-of-the-week section. Looks like that was a darn good deal :cool:
nuzzle
December 31st, 2010, 07:40 AM
The problem I see with this is that the assignment operator breaks the semantics of the const variable.
If you want to support assignment then the member variable shouldn't be const.
I know this is a zombie thread but I find it quite interesting so I reply anyway.
Many felt the strict physical interpretation of C++ constness was too rigid and so the mutable keyword was introduced. It allows for a more logical interpretation of constness and in my view assignment of const objects falls well into this category. I can't see why it should be considered more malign than using the mutable keyword really.
Say you have a small mutable value object like this,
struct Mutable {
int a;
Mutable() : a(0) {}
explicit Mutable(int a) : a(a) {}
};
If you want to make it immutable a natural approach would be to simply declare 'a' const,
struct Immutable {
const int a; // variable is declared const
Immutable() : a(0) {}
explicit Immutable(int a) : a(a) {}
};
Now the 'a' variable cannot be changed after object creation which is what you want. What you probably don't want is that the object is forever stuck in the variable where it was created. You cannot do this,
Immutable m1(43);
Immutable m2(7);
m2 = m1; // not allowed
m1 = m2; // not allowed
The m1 and m2 variables behave like if they were declared const which they aren't so this definately is more constness than you bargained for.
To overcome this you have two options. One is to supply an assignment operator which casts away const, like this,
Now objects are immutable so the 'a' variable can never change after creation but objects can still be copied and assigned to other variables.
The other option is to make 'a' private and non-const and access it via a const getter function like,
struct Immutable2 {
int a() const { // a getter
return a_;
}
Immutable2() : a_(0) {}
explicit Immutable2(int a) : a_(a) {}
private:
int a_; // private non-const
};
Well, before I came upon this thread I probably would've preferred the second approach but I'm not so sure anymore. Okay, the cast in the first approach is ugly but it has the advantage of the 'a' variable actually being declared const and thus safer from any accidental modification.
What do you think? Maybe there's some third option?
D_Drmmr
December 31st, 2010, 09:28 AM
To overcome this you have two options. One is to supply an assignment operator which casts away const, like this,
As mentioned above, this is undefined behavior according to the standard.
Now objects are immutable so the 'a' variable can never change after creation but objects can still be copied and assigned to other variables.
The value of 'a' can change, namely through the assignment operator. Which is exactly what is bad about this IMO. If I see a member variable is const, I expect it won't change, because that is exactly what it means. So IMO declaring a variable as const when it actually can change isn't an advantage, but a bug waiting to happen.
What do you think? Maybe there's some third option?
A third option could be to check in run-time whether the value of the const member variables is equal.
struct Foo
{
int m_a;
const int m_b;
explicit Foo(int b) : m_b(b) {}
Foo& operator =(const Foo& that) {
if (m_b != that.m_b) {
throw std::invalid_argument("Cannot assign incompatible Foo.");
}
m_a = that.m_a;
}
};
nuzzle
January 1st, 2011, 03:00 AM
As mentioned above, this is undefined behavior according to the standard.
Two posters recommend this solution and one is unsure. Now you indicate you're sure that,
leads to undefined behavior according to the standard. Could you please point out the section you're referring to. If that's true then this "solution" is out of question of course.
If I see a member variable is const, I expect it won't change, because that is exactly what it means. So IMO declaring a variable as const when it actually can change isn't an advantage, but a bug waiting to happen.
Well, the 'a' variable itself of a created object never changes. Both Immutable1 and Immutable2 are immutable indeed. It's just that the far-reaching restrictions of physical constness are somewhat relaxed (just like they are when mutable is used). You still have logical constness of the 'a' variable. It can never change.
I find it both unexpected and unwanted that just because you declare a variable const then objects cannot be freely assigned anymore. Consider my Immutable2 example. You cannot declare the 'a_' variable const although it's a private constant that should never change after object creation. Because if you do, Immutable2 variables will behave as if they were declared const although they aren't.
So I think physical constness of variables induces an unhealthy coupling between variables and values. One interesting suggesting to resolve this at the language level was presented in this thread,
struct Suggestion {
mutable const a;
};
It would mean that although 'a' never changes once a Suggestion object is created, it's still possible to copy Suggestion objects between Suggestion variables.
Well, it looks like I'm back to the Immutable2 solution. Especially if Immutable1 gives undefined behavior as you claim.
D_Drmmr
January 1st, 2011, 09:55 AM
Two posters recommend this solution and one is unsure. Now you indicate you're sure that,
leads to undefined behavior according to the standard. Could you please point out the section you're referring to. If that's true then this "solution" is out of question of course.
All I happen to have here is the draft n2798, but I guess that'll do. Section 7.1.6.1 states in point 4:
Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime results in undefined behavior.
It is followed by an example:
const int* ciq = new const int (3); // initialized as required
int* iq = const_cast<int*>(ciq); // cast required
*iq = 4; // undefined: modifies a const object
Well, the 'a' variable itself of a created object never changes. Both Immutable1 and Immutable2 are immutable indeed. It's just that the far-reaching restrictions of physical constness are somewhat relaxed (just like they are when mutable is used). You still have logical constness of the 'a' variable. It can never change.
I don't understand what you mean. If we take your example that uses const_cast as a valid program (e.g. by assuming a compiler extension that allows this), then the value of 'a' can change.
Immutable1 m1(43);
Immutable1 m2(7);
m2 = m1;
Before the assignment the value of m2.a is 7, after the assignment it is 43. That means it changed. That's bad, because 'a' was declared const.
I find it both unexpected and unwanted that just because you declare a variable const then objects cannot be freely assigned anymore. Consider my Immutable2 example. You cannot declare the 'a_' variable const although it's a private constant that should never change after object creation. Because if you do, Immutable2 variables will behave as if they were declared const although they aren't.
In this case, in a sense yes, but that is only because there is only one member variable. That's why I gave an example with two member variables.
So I think physical constness of variables induces an unhealthy coupling between variables and values. One interesting suggesting to resolve this at the language level was presented in this thread,
struct Suggestion {
mutable const a;
};
It would mean that although 'a' never changes once a Suggestion object is created, it's still possible to copy Suggestion objects between Suggestion variables.
Besides the contradiction in your last sentense, I don't see what's the point. What's the difference with having a non-const member variable? If the idea is that only the copy assignment operator of Suggestion is allowed to change the value of a, then what's the added value over the Immutable2 example you gave?
monarch_dodra
January 3rd, 2011, 02:22 AM
Modifying a const object is and has always been undefined. This is because the compiler usually makes some assumptions on the const member, moves the data in register, and never checks the value again. Simply declare a const integer. Change that integer's value cia const_cast, print the integer: Surprise!
However, it is completly legal to modify a non-const object via a const handle (for example a const pointer), by const_castiung the handle. This is very immoral though. The reason you should never do this is because there is no way to know if the underlying object actually is or isn't const.
Back to the subject at hand: const is a contract. By using const cast, you are breaching the contract, rendering it void, and useless. casting away a const undermines the very concept of what a const is, and as such, should never be done.
The only time I have ever seen a const_cast safely in action is to implement "&operator[]" in terms of "const& operator[] const". Still, this is only an implementation detail, and a coding trick.
----
IMO: The real question when using const is "I would like to write a constness contract: who am I writing this contract with?"
-I'm writing the constract with my users: The interface should be const-correct. The const-ness of my (private) members is then irrelevent. As long as my users don't see a change, I can do whatever I want. This is where mutable comes in.
-I'm writing a constract with my fellow developers: My goal is to make a design that may no be breached by anyone: My objects are set in stone. If I do not respect this contract, then I am making it irrelevant and should not have made it in the first place.
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.