It is an open ended question.
Effective C++. Item 3. Use const whenever possible. Really?
I would like to make anything which doesn't change during the objects lifetime const. But const comes with it own troubles. If a class has any const member, the compiler generated assignment operator is disabled. Without an assignment operator a class won't work with STL. If you want to provide your own assignment operator, const_cast is required. That means more hustle and more room for error. How often you use const class members?
EDIT: As a rule, I strive for const correctness because I do a lot of multithreading. I rarely need to implemented copy control for my classes and never code delete (unless it is absolutely necessary). I feel that the current state of affairs with const contradicts my coding style. Const forces me to implement assignment operator even though I don't need one. Even without const_cast assignment is a hassle. You need to make sure that all const members compare equal and then manually copy all non-const member.
Code. Hope it will clarify what I mean. The class you see below won't work with STL. You need to implement an assignment for it, even though you don't need one.
class Multiply {
public:
Multiply(double coef) : coef_(coef) {}
double operator()(double x) const {
return coef_*x;
}
private:
const double coef_;
};
You said yourself that you make const "anything which doesn't change during the objects lifetime". Yet you complain about the implicitly declared assignment operator getting disabled. But implicitly declared assignment operator does change the contents of the member in question! It is perfectly logical (according to your own logic) that it is getting disabled. Either that, or you shouldn't be declaring that member const.
Also, providing you own assignment operator does not require a const_cast. Why? Are you trying to assign to the member you declared const inside your assignment operator? If so, why did you declare it const then?
In other words, provide a more meaningful description of the problems you are running into. The one you provided so far is self-contradictory in the most obvious manner.
As AndreyT pointed out, under these circumstances assignment (mostly) doesn't make a lot of sense. The problem is that vector (for one example) is kind of an exception to that rule.
Logically, you copy an object into the vector, and sometime later you get back another copy of the original object. From a purely logical viewpoint, there's no assignment involved. The problem is that vector requires that the object be assignable anyway (actually, all C++ containers do). It's basically making an implementation detail (that somewhere in its code, it might assign the objects instead of copying them) part of the interface.
There is no simple cure for this. Even defining your own assignment operator and using const_cast doesn't really fix the problem. It's perfectly safe to use const_cast when you get a const pointer or reference to an object that you know isn't actually defined to be const. In this case, however, the variable itself is defined to be const -- attempting to cast away the constness and assign to it gives undefined behavior. In reality, it'll almost always work anyway (as long as it's not static const with an initializer that's known at compile time), but there's no guarantee of it.
C++ 11 and newer add a few new twists to this situation. In particular, objects no longer need to be assignable to be stored in a vector (or other collections). It's sufficient that they be movable. That doesn't help in this particular case (it's no easier to move a const object than it is to assign it) but does make life substantially easier in some other cases (i.e., there are certainly types that are movable but not assignable/copyable).
In this case, you could use a move rather than a copy by adding a level of indirection. If your create an "outer" and an "inner" object, with the const member in the inner object, and the outer object just containing a pointer to the inner:
struct outer {
struct inner {
const double coeff;
};
inner *i;
};
...then when we create an instance of outer, we define an inner object to hold the const data. When we need to do an assignment, we do a typical move assignment: copy the pointer from the old object to the new one, and (probably) set the pointer in the old object to a nullptr, so when it's destroyed, it won't try to destroy the inner object.
If you wanted to badly enough, you could use (sort of) the same technique in older versions of C++. You'd still use the outer/inner classes, but each assignment would allocate a whole new inner object, or you'd use something like a shared_ptr to let the outer instances share access to a single inner object, and clean it up when the last outer object is destroyed.
It doesn't make any real difference, but at least for the assignment used in managing a vector, you'd only have two references to an inner while the vector was resizing itself (resizing is why a vector requires assignable to start with).
I very rarely use them - the hassle is too great. Of course I always strive for const correctness when it comes to member functions, parameters or return types.
Errors at compile time are painful, but errors at runtime are deadly. Constructions using const might be a hassle to code, but it might help you find bugs before you implement them. I use consts whenever possible.
I try my best to follow the advice of using const whenever possible, however I agree that when it comes to class members, const is a big hassle.
I have found that I am very careful with const-correctness when it comes to parameters, but not as much with class members. Indeed, when I make class members const and it results in an error (due to using STL containers), the first thing I do is remove the const.
I'm wondering about your case... Everything below is but supposition because you did not provide the example code describing your problem, so...
The cause
I guess you have something like:
struct MyValue
{
int i ;
const int k ;
} ;
IIRC, the default assignment operator will do a member-by-member assignment, which is akin to :
MyValue & operator = (const MyValue & rhs)
{
this->i = rhs.i ;
this->k = rhs.k ; // THIS WON'T WORK BECAUSE K IS CONST
return *this ;
} ;
Thus, this won't get generated.
So, your problem is that without this assignment operator, the STL containers won't accept your object.
As far I as see it:
The compiler is right to not generate this operator =
You should provide your own, because only you know exactly what you want
You solution
I'm afraid to understand what do you mean by const_cast.
My own solution to your problem would be to write the following user defined operator :
MyValue & operator = (const MyValue & rhs)
{
this->i = rhs.i ;
// DON'T COPY K. K IS CONST, SO IT SHOULD NO BE MODIFIED.
return *this ;
} ;
This way, if you'll have:
MyValue a = { 1, 2 }, b = {10, 20} ;
a = b ; // a is now { 10, 2 }
As far as I see it, it is coherent. But I guess, reading the const_cast solution, that you want to have something more like:
MyValue a = { 1, 2 }, b = {10, 20} ;
a = b ; // a is now { 10, 20 } : K WAS COPIED
Which means the following code for operator =:
MyValue & operator = (const MyValue & rhs)
{
this->i = rhs.i ;
const_cast<int &>(this->k) = rhs.k ;
return *this ;
} ;
But, then, you wrote in your question:
I would like to make anything which doesn't change during the objects lifetime const
With what I supposed is your own const_cast solution, k changed during the object lifetime, which means that you contradict yourself because you need a member variable that doesn't change during the object lifetime unless you want it to change!
The solution
Accept the fact your member variable will change during the lifetime of its owner object, and remove the const.
you can store shared_ptr to your const objects in STL containers if you'd like to retain const members.
#include <iostream>
#include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
#include <vector>
class Fruit : boost::noncopyable
{
public:
Fruit(
const std::string& name
) :
_name( name )
{
}
void eat() const { std::cout << "eating " << _name << std::endl; }
private:
const std::string _name;
};
int
main()
{
typedef boost::shared_ptr<const Fruit> FruitPtr;
typedef std::vector<FruitPtr> FruitVector;
FruitVector fruits;
fruits.push_back( boost::make_shared<Fruit>("apple") );
fruits.push_back( boost::make_shared<Fruit>("banana") );
fruits.push_back( boost::make_shared<Fruit>("orange") );
fruits.push_back( boost::make_shared<Fruit>("pear") );
BOOST_FOREACH( const FruitPtr& fruit, fruits ) {
fruit->eat();
}
return 0;
}
though, as others have pointed out it's somewhat of a hassle and often easier in my opinion to remove the const qualified members if you desire the compiler generated copy constructor.
I only use const on reference or pointer class members. I use it to indicate that the target of the reference or pointer should not be changed. Using it on other kinds of class members is a big hassle as you found out.
The best places to use const is in function parameters, pointers and references of all kinds, constant integers and temporary convenience values.
An example of a temporary convenience variable would be:
char buf[256];
char * const buf_end = buf + sizeof(buf);
fill_buf(buf, buf_end);
const size_t len = strlen(buf);
That buf_end pointer should never point anywhere else so making it const is a good idea. The same idea with len. If the string inside buf never changes in the rest of the function then its len should not change either. If I could, I would even change buf to const after calling fill_buf, but C/C++ does not let you do that.
The point is that the poster wants const protection within his implementation but still wants the object assignable. The language does not support such semantics conveniently as constness of the member resides at the same logical level and is tightly coupled with assignability.
However, the pImpl idiom with a reference counted implementation or smart pointer will do exactly what the poster wants as assignability is then moved out of the implementation and up a level to the higher level object. The implementation object is only constructed/destructed whence assignment is never needed at the lower level.
I think your statement
If a class has const any member, the
compiler generated assignment operator
is disabled.
Might be incorrect. I have classes that have const method
bool is_error(void) const;
....
virtual std::string info(void) const;
....
that are also used with STLs. So perhaps your observation is compiler dependent or only applicable to the member variables?
I would only use const member iff the class itself is non-copyable. I have many classes that I declare with boost::noncopyable
class Foo : public boost::noncopyable {
const int x;
const int y;
}
However if you want to be very sneaky and cause yourself lots of potential
problems you can effect a copy construct without an assignment but you have to
be a bit careful.
#include <new>
#include <iostream>
struct Foo {
Foo(int x):x(x){}
const int x;
friend std::ostream & operator << (std::ostream & os, Foo const & f ){
os << f.x;
return os;
}
};
int main(int, char * a[]){
Foo foo(1);
Foo bar(2);
std::cout << foo << std::endl;
std::cout << bar<< std::endl;
new(&bar)Foo(foo);
std::cout << foo << std::endl;
std::cout << bar << std::endl;
}
outputs
1
2
1
1
foo has been copied to bar using the placement new operator.
It isn't too hard. You shouldn't have any trouble making your own assignment operator. The const bits don't need to be assigned (as they're const).
Update
There is some misunderstanding about what const means. It means that it will not change, ever.
If an assignment is supposed to change it, then it isn't const.
If you just want to prevent others changing it, make it private and don't provide an update method.
End Update
class CTheta
{
public:
CTheta(int nVal)
: m_nVal(nVal), m_pi(3.142)
{
}
double GetPi() const { return m_pi; }
int GetVal() const { return m_nVal; }
CTheta &operator =(const CTheta &x)
{
if (this != &x)
{
m_nVal = x.GetVal();
}
return *this;
}
private:
int m_nVal;
const double m_pi;
};
bool operator < (const CTheta &lhs, const CTheta &rhs)
{
return lhs.GetVal() < rhs.GetVal();
}
int main()
{
std::vector<CTheta> v;
const size_t nMax(12);
for (size_t i=0; i<nMax; i++)
{
v.push_back(CTheta(::rand()));
}
std::sort(v.begin(), v.end());
std::vector<CTheta>::const_iterator itr;
for (itr=v.begin(); itr!=v.end(); ++itr)
{
std::cout << itr->GetVal() << " " << itr->GetPi() << std::endl;
}
return 0;
}
Philosophically speaking, it looks as safety-performance tradeoff. Const used for safety. As I understand, containers use assignment to reuse memory, i.e. for sake of performance. They would may use explicit destruction and placement new instead (and logicaly it is more correct), but assignment has a chance to be more efficient. I suppose, it is logically redundant requirement "to be assignable" (copy constructable is enough), but stl containers want to be faster and simpler.
Of course, it is possible to implement assignment as explicit destruction+placement new to avoid const_cast hack
Rather than declaring the data-member const, you can make the public surface of the class const, apart from the implicitly defined parts that make it (semi)regular.
class Multiply {
public:
Multiply(double coef) : coef(coef) {}
double operator()(double x) const {
return coef*x;
}
private:
double coef;
};
You basically never want to put a const member variable in a class. (Ditto with using references as members of a class.)
Constness is really intended for your program's control flow -- to prevent mutating objects at the wrong times in your code. So don't declare const member variables in your class's definition, rather make it all or nothing when you declare instances of the class.
Related
Programming in C++, I often want to give the user of a class read-only access to an attribute, and the class itself read-write access. I hate XxxGet() methods, so I often use a public const & to a private attribute, like this:
class counter {
private:
int _count;
public:
const int & count;
counter : _count( 0 ), count( _count ){}
void inc( void ){ _counter++; }
};
Is there a common name for this trick?
My personal name for that trick would be bad idea.
I would avoid the approach that you are following, as it incurs extra unneeded cost. If you add accessors they can be inlined as needed, with the only penalty of having to type an extra pair of parentheses:
class counter {
int _count;
public:
counter() : _count() {}
int count() const { return _count; }
void inc() { ++_count; }
};
The main difference is that in your solution you are incrementing the size of the object by one reference (for most implementations this means pointer), and then each access requires an extra indirection. On the other hand, with the accessor, the actual variable is used, the function will be optimized away (inlined, and resolved to a single read to the variable).
As of a proper name for that type of construct, well, I have never seen your particular construct in C++, but if you consider other languages, that is the basic concept of a property in C#, where you can make the getter public and the setter private.
EDIT: I guess that bad idea can be misinterpreted as just a personal opinion (which it is), but consider the side effects of that design:
Because of the reference in the object, you inhibit the implicit definition of the assignment operator. Much worse, the copy constructor will compile but not work as expected:
// consider the implementation with the const reference
counter c1;
counter c2( c1 ); // compiles, so it must work
c2.inc();
std::cout << c2.count; // outputs 0
// c2 = c1; // error: well, at least this does not compile!
The problem is that the compiler generated copy constructor will make the count reference in c2 refer to the same int that the count reference in c1 refers to, which might lead to hard-to-find subtle issues in your code that are actually quite hard to debug.
Edit
Just now I thought of a name that could be considered the same pattern. Though not typically used for member variables.
There could actually be a name for this, as has been made popular by the Boost Tuple library as well as the TR1/C++11 implementations:
Tieing
Typical example:
tuple<int> tie(ref(some_var));
// or shorter:
auto tied = tie(var1, var2, var3);
Assignment complications
The closest name for this (anti?) pattern I could _immediately think of before, is: pointer or reference aliasing. It is not a very good idea for many reasons, some of which have been mentioned
class layout + size
copy/assignment semantics
compiler optimizations: the compiler will shun from making assumptions about the value of (register-allocated) variables when it knows references could point to the same memory location.
In addition to the points David makes, the compiler will be unable to generate default
semantically valid copy constructor
assignment operator
move assignment operator
for your class now that contains references. Note also that your class can't possibly be POD anymore
A number of others have already condemned this idea, and I (mostly) tend to agree with them. Although quite a few people probably dislike it (at least) as much, if I was going to support something on this order, I'd do something like this:
class counter {
int count_;
public:
counter(int init=0) : count_(init) {}
operator int() const { return count_; }
void inc() { ++count_; }
};
The one problem with this is one that's shared with implicit conversions in general: that the implicit conversion can happen even when you don't want it to. OTOH, the fact that it's a user-supplied conversion actually eliminates many of the problems -- only one implicit conversion will happen automatically in any given situation, so (for example) the fact that you've supplied a conversion to int will not mean that a counter with a value of 0 can be implicitly converted from a counter to an int to a (null) pointer to T, because that would involve two implicit conversions.
There are times this can cause a problem anyway, in which case (as of C++11) you can make the conversion operator explicit, so it'll only happen when/if the user does an explicit conversion like:
counter t;
int x = t; // allowed by code above, but not with `explicit` conversion operator.
int y = static_cast<int>(t); // allowed with `explicit` conversion operator.
I have a class that should hold a reference to some data, without owning that data (i.e. the actual data is guaranteed not to go out of scope). In particular, the class cannot make a copy – the data is easily several gigabytes in size.
Now, the usual implementation (I assume) is to have a reference to the data:
struct holder_ref {
type const& value;
holder_ref(type const& value) : value(value) { }
};
(Please note that the constness has absolutely no bearing on the problem).
Now, I absolutely need this class to be assignable (i.e. have a working operator =). I thought this was a fairly common problem but I can’t remember how (if ever) I’ve solved it before.
The problem is that a reference cannot be assigned and there’s simply no way around this. The only solution I’ve come up with uses placement new in place of the assignment operator:
// x = other_x; gets replaced with:
x.~T();
new (&x) T(other_x);
Now, this works and is standard compliant. But it sure is ugly. No – inacceptable.
So I’m searching for alternatives. One idea is to use pointers, but I’m unsure whether my constructor is actually guaranteed to work (and passing a pointer is impossible due to the interface I have to adhere to):
struct holder_ptr {
type const* value;
// Is this legal?
holder_ptr(type const& value = 0) : value(&value) { }
};
But I’d rather use a reference, if at all possible. Only – how to implement the assignment operator?
struct holder_ref {
type const& value;
holder_ref(type const& value = 0) : value(value) { }
holder_ref& operator =(holder_ref const& other) {
// Now what?!
return *this;
}
};
As a test case, consider the following code:
int main() {
int const TEST1 = 23;
int const TEST2 = 13;
int const TEST3 = 42;
std::vector<holder_ptr> hptr(1);
std::vector<holder_ref> href(2);
// Variant 1. Pointer.
hptr[0] = holder_ptr(TEST1);
// Variant 2. Placement new.
href[0].~holder_ref();
new (&href[0]) holder_ref(TEST2);
// Variant 3. ???
href[1] = holder_ref(TEST3);
assert(*hptr[0].value == TEST1); // Works (?)
assert(href[0].value == TEST2); // Works
assert(href[1].value == TEST3); // BOOM!
}
(Also, just to make this clear – the type we’re talking about is non-POD and I need a standard compliant solution.)
I don't see anything wrong with using a holder_ptr. It can be implemented something like so:
struct bad_holder : std::exception { };
struct holder_ptr {
holder_ptr() : value(0) { }
holder_ptr(type const& value) : value(&value) { }
type const& get() {
if (value == 0) throw bad_holder();
return *value;
}
private:
type const* value;
};
So long as you always assign to the pointer from a reference, you know that you have a valid object (that, or you ended up with a "null reference" previously, in which case you have other, bigger problems since you'll already have invoked undefined behavior).
With this solution, the interface is implemented entirely in terms of references, but under the hood a pointer is used so that the type is assignable. The use of references in the interface ensures there are none of the concerns that come with using pointers (namely, you never have to worry whether the pointer is null).
Edit: I've updated the example to allow for the holder to be default constructible.
I'd use the pointer holder. But if you are dead set against that, how about hiding your placement new operator=:
holder_ref& operator =(holder_ref const& other) {
new (this) holder_ref(other);
return *this;
}
Is a TR1 weak_ptr standard compliant enough?
I have several members in my class which are const and can therefore only be initialised via the initialiser list like so:
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 internally defined container classes (e.g. vectors), and these containers require that operator= is defined in the class.
Of course, my operator= needs to do something like this:
MyItemT&
MyItemT::operator=(const MyItemT& other)
{
mMyPacket = other.mPacket;
mMyInfo = other.mMyInfo;
return *this;
}
which of course doesn't work because mMyPacket and mMyInfo are const members.
Other than making these members non-const (which I don't want to do), any ideas about how I could fix this?
You're kind of violating the definition of const if you have an assignment operator that can change them after construction has finished. If you really need to, I think Potatoswatter's placement new method is probably best, but if you have an assignment operator your variables aren't really const, since someone could just make a new instance and use it to change their values
Rather than storing objects in your containers directly, you might be able to store pointers (or smart pointers). That way, you don't have to mutate any of the members of your class -- you get back exactly the same object as you passed in, const and all.
Of course, doing this will probably change the memory management of your application somewhat, which may well be a good enough reason not to want to.
It's a dirty hack, but you can destroy and reconstruct 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: lest I destroy my credibility, I don't actually do this myself, I would remove the const. However, I've been debating changing the practice, because const simply is useful and better to use wherever possible.
Sometimes there is a distinction between the resource and the value represented by an object. A member may be const through changes to value as long as the resource is the same, and it would be nice to get compile-time safety on that.
Edit 2: #Charles Bailey has provided this wonderful (and highly critical) link: http://gotw.ca/gotw/023.htm.
Semantics are tricky in any derived class operator=.
It may be inefficient because it doesn't invoke assignment operators that have been defined.
It's incompatible with wonky operator& overloads (whatever)
etc.
Edit 3: Thinking through the "which resource" vs "what value" distinction, it seems clear that operator= should always change the value and not the resource. The resource identifier may then be const. In the example, all the members are const. If the "info" is what's stored inside the "packet," then maybe the packet should be const and the info not.
So the problem isn't so much figuring out the semantics of assignment as lack of an obvious value in this example, if the "info" is actually metadata. If whatever class owns a MyItemT wants to switch it from one packet to another, it needs to either give up and use an auto_ptr<MyItemT> instead, or resort to a similar hack as above (the identity test being unnecessary but the catch remaining) implemented from outside. But operator= shouldn't change resource binding except as an extra-special feature which absolutely won't interfere with anything else.
Note that this convention plays well with Sutter's advice to implement copy construction in terms of assignment.
MyItemT::MyItemT( MyItemT const &in )
: mMyPacket( in.mMyPacket ) // initialize resource, const member
{ *this = in; } // assign value, non-const, via sole assignment method
I think you could get away 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 would have Const<MyPacketT>. Not that the interface only provides one way to change it: through an explicit call to reset.
I think any use of mMyPacket.reset can easily be search for. As #MSalters said it protects against Murphy, not Machiavelli :)
You might consider making the MyPacketT and MyInfoT members be pointers to const (or smart pointers to const). This way the data itself is still marked const and immutable, but you can cleanly 'swap' to another set of const data in an assignment if that makes sense. In fact, you can use the swap idiom to perform the assignment in an exception safe manner.
So you get the benefit of const to help you prevent accidentally allowing changes that you want the design to prevent, but you still allow the object as a whole to be assigned from another object. For example, this will let you use objects of this class in STL containers.
You might look at this as a special application of the 'pimpl' idiom.
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 representation of what the OP intended. However, if the 'reassignable' const members can be shared (and that's probably true, given my understanding of why the OP wants them const), then it might be an optimization to use shared_ptr<>'s and let the copy and assignment operations of the shared_ptr<> class take care of things for those objects - if you have no other members that require special copy or assign sematics, then your class just got a lot simpler, and you might even save a significant bit of memory usage by being able to share copies of the MyPacketT and MyInfoT objects.
Given a class like this:
class Foo
{
const int a;
};
Is it possible to put that class in a vector? When I try, my compiler tells me it can't use the default assignment operator. I try to write my own, but googling around tells me that it's impossible to write an assignment operator for a class with const data members. One post I found said that "if you made [the data member] const that means you don't want assignment to happen in the first place." This makes sense. I've written a class with const data members, and I never intended on using assignment on it, but apparently I need assignment to put it in a vector. Is there a way around this that still preserves const-correctness?
I've written a class with const data members, and I never intended on using assignment on it, but apparently I need assignment to put it in a vector. Is there a way around this that still preserves const-correctness?
You have to ask whether the following constraint still holds
a = b;
/* a is now equivalent to b */
If this constraint is not true for a and b being of type Foo (you have to define the semantics of what "equivalent" means!), then you just cannot put Foo into a Standard container. For example, auto_ptr cannot be put into Standard containers because it violates that requirement.
If you can say about your type that it satisfies this constraint (for example if the const member does not in any way participate to the value of your object, but then consider making it a static data member anyway), then you can write your own assignment operator
class Foo
{
const int a;
public:
Foo &operator=(Foo const& f) {
/* don't assign to "a" */
return *this;
}
};
But think twice!. To me, it looks like that your type does not satisfy the constraint!
Use a vector of pointers std::vector<Foo *>. If you want to avoid the hassle of cleaning up after yourself, use boost::ptr_vector.
Edit: My initial stab during my coffee break, static const int a; won't work for the use case the OP has in mind, which the initial comments confirm, so I'm rewriting and expanding my answer.
Most of the time, when I want to make an element of a class constant, it's a constant whose value is constant for all time and across all instances of the class. In that case, I use a static const variable:
class Foo
{
public:
static const int a;
};
Those don't need to be copied among instances, so if it applied, that would fix your assignment problem. Unfortunately, the OP has indicated that this won't work for the case the OP has in mind.
If you want to create a read-only value that clients can't modify, you can make it a private member variable and only expose it via a const getter method, as another post on this thread indicates:
class Foo
{
public:
int get_a() const { return a; }
private:
int a;
};
The difference between this and
class Foo
{
public:
const int a;
};
is:
The const int gives you assurance that not even the implementation of the class will be able to muck with the value of a during the lifetime of the object. This means that assignment rightfully won't work, since that would be trying to modify the value of a after the object's been created. (This is why, btw, writing a custom operator=() that skips the copy of a is probably a bad idea design-wise.)
The access is different – you have to go through a getter rather than accessing the member directly.
In practice, when choosing between the two, I use read-only members. Doing so probably means you'll be able to replace the value of an object with the value of another object without violating semantics at all. Let's see how it would work in your case.
Consider your Grid object, with a width and height. When you initially create the vector, and let's say you reserve some initial space using vector::reserve(), your vector will be populated with initial default-initialized (i.e. empty) Grids. When you go to assign to a particular position in the vector, or push a Grid onto the end of the vector, you replace the value of the object at that position with a Grid that has actual stuff. But you may be OK with this! If the reason you wanted width and height to be constant is really to ensure consistency between width and height and the rest of the contents of your Grid object, and you've verified that it doesn't matter whether width and height are replaced before or after other elements of Grid are replaced, then this assignment should be safe because by the end of the assignment, the entire contents of the instance will have been replaced and you'll be back in a consistent state. (If the lack of atomicity of the default assignment was a problem, you could probably get around this by implementing your own assignment operator which used a copy constructor and a swap() operation.)
In summary, what you gain by using read-only getters is the ability to use the objects in a vector or any container with value semantics. However, it then falls to you to ensure that none of Grid's internal operations (or the operations of friends of Grid) violate this consistency, because the compiler won't be locking down the width and height for you. This goes for default construction, copy construction, and assignment as well.
I'm considering making the data member non-const, but private and only accessible by a get function, like this:
class Foo
{
private:
int a;
public:
int getA() const {return a;}
};
Is this 'as good' as const? Does it have any disadvantages?
As of c++20, using const member variables are legal without restrictions that had made it virtually unusable in containers. You still have to define a copy assignment member function because it continues to be automatically deleted when a const object exists in the class. However, changes to "basic.life" now allow changing const sub-objects and c++ provides rather convenient functions for doing this. Here's a description of why the change was made:
The following code shows how to define a copy assignment member function which is useable in any class containing const member objects and uses the new functions std::destroy_at and std::construct_at to fulfil the requirement so the new "basic.life" rules. The code demonstrates assignment of vectors as well as sorting vectors with const elements.
Compiler explorer using MSVC, GCC, CLANG https://godbolt.org/z/McfcaMWqj
#include <memory>
#include <vector>
#include <iostream>
#include <algorithm>
class Foo
{
public:
const int a;
Foo& operator=(const Foo& arg) {
if (this != &arg)
{
std::destroy_at(this);
std::construct_at(this, arg);
}
return *this;
}
};
int main()
{
std::vector<Foo> v;
v.push_back({ 2 });
v.push_back({ 1 });
v.insert(v.begin() + 1, Foo{ 0 });
std::vector<Foo> v2;
v2 = v;
std::sort(v2.begin(), v2.end(), [](auto p1, auto p2) {return p1.a < p2.a; });
for (auto& x : v2)
std::cout << x.a << '\n';
}
I want to implement a Swap() method for my class (let's call it A) to make copy-and-swap operator=(). As far as I know, swap method should be implemented by swapping all members of the class, for example:
class A
{
public:
void swap(A& rhv)
{
std::swap(x, rhv.x);
std::swap(y, rhv.y);
std::swap(z, rhv.z);
}
private:
int x,y,z;
};
But what should I do if I have a const member? I can't call std::swap for it, so I can't code A::Swap().
EDIT: Actually my class is little bit more complicated. I want to Serialize and Deserialize it. Const member is a piece of data that won't change (its ID for example) within this object. So I was thinking of writing something like:
class A
{
public:
void Serialize(FILE* file) const
{
fwrite(&read_a, 1, sizeof(read_a), file);
}
void Deserialize(FILE* file) const
{
size_t read_a;
fread(&read_a, 1, sizeof(read_a), file);
A tmp(read_a);
this->Swap(tmp);
}
private:
const size_t a;
};
and call this code:
A a;
FILE* f = fopen(...);
a.Deserialize(f);
I'm sorry for such vague wording.
I think what you really want is to have an internal data structure that you can easily exchange between objects. For example:
class A
{
private:
struct A_Data {
int x;
int y;
const int z;
A_Data(int initial_z) : z(initial_z) {}
};
std::auto_ptr<A_Data> p_data;
public:
A(int initial_z) : p_data(new A_Data(initial_z)) {}
void swap(A& rhv) {
std::swap(p_data, rhv.p_data);
}
};
This keeps the z value constant within any instance of A object internal data, but you can swap the internal data of two A objects (including the constant z value) without violating const-correctness.
After a good nights sleep I think the best answer is to use a non-const pointer to a const value -- after all these are the semantics you are trying to capture.
f0b0s, a good design principle is to design your objects to be immutable. This means that the object can't change once created. To "change" the object, you must copy the object and make sure to change the elements you want.
That being said, in this case you should look at using a copy constructor instead to copy the objects you want to swap, and then actually swap the references to the object. I can understand it'd be tempting just to be able to change the elements of an object under the hood, but it'd be better to make a copy of the object and replace the references to that object with the NEW object instead. This gets you around any const nastiness.
Hope this helps.
I suggest you use pointers to the instances. The pointers can be swapped much easier than the data in the class or struct.
The only way to swap a constant value is to create another object, or clone the current object.
Given a struct:
struct My_Struct
{
const unsigned int ID;
std::string name;
My_Struct(unsigned int new_id)
: ID(new_id)
{ ; }
};
My understanding is that you want to swap instances of something like My_Struct above. You can copy the mutable (non-const) members but not the const member. The only method to alter the const member is to create a new instance with a new value for the const member.
Perhaps you need to rethink your design.
IMHO you must consider not to swap CONST members.
PD: I think you could consider to use reflection in your approach. so you don't have to maintain the function.
This is why const_cast was created. Just remember not to shoot your foot off.
Edit: OK, I concede - const_cast wasn't made for this problem at all. This might work with your compiler, but you can't count on it and if demons come flying out of your nostrils, please don't blame me.
tl;dr; : It's Undefined Behavior.
Reference/reason: CppCon 2017: Scott Schurr “Type Punning in C++17: Avoiding Pun-defined Behavior, #24m52s +- ”
My interpretation, by example:
Suppose you create an object of type T, which have some const members. You can pass this object as a non-const reference to a function f(&T) that manipulates it, but you'd expect the const members to remain unalterable after the call. swap can be called in non-const references, and it can happen inside the function f, breaking the premise of const members to the caller.
Every part of your code that uses swap would have to assert that the object of type T being swapped does not belong to any context where the const members are assumed constant. That is impossible to automatically verify*.
*I just assumed that this is impossible to verify because it seems like an extension of the undecidability of the halting problem.