Accordingly to C++ best practices on IsoCpp we shouldn't have a const or reference data member: C.12: Don’t make data members const or references
But it does not specify if a pointer to a const object is allowed or not. Consider the example:
class Connection {
gsl::not_null<const Network*> m_network;
...
public:
[[nodiscard]] const Network* getNetwork() const;
...
}
// Implementation
const Network* Connection::getNetwork() const {
return m_network;
}
Is it still conformant with the best practice described? The pointer isn't const but the data the pointer points to is.
Should we remove const from member declaration but still mark the getter return type as a pointer to const?
It depends.
The reason this recommendation exists, is to make your type copyable and assignable, with both having matching semantics.
So consider this:
struct Connection {
Network const& network;
/**/
};
Copying this is perfectly fine, right? But then assigning breaks because network will not be reseatable.
So in this case, replacing that member with an e.g. std::shared_ptr<Network const> is fine. Presumably several connections can use the same network.
But -- if you do not want sharing but each object to have its own copy of the member, then you are left with reconstructing that member on assignment, because the old object cannot be changed. Depending on the object that might not be what you want to do.
For example:
struct Connection {
std::shared_ptr<Network const> network;
std::unique_ptr<std::vector<Entry> const> properties;
};
Aside from a pointer-to-vector being bad form (double indirection for no good reason). Reconstructing a vector anew requires a new memory allocation, while just reusing the old will not (assuming there was enough memory allocated).
Granted, this is a bit of constructed example, and there would maybe be a way to go around that. But the point is, that the similarty of your copy and assignment will heavily depend on the quality of the copy and assignment of the member object.
In such a case the same reasoning that applied to the situation without the pointer similarly applies to the situation with the pointer. You will have to decide whether that is acceptable for your type.
However, I hold that not every type must be assignable. You can make the conscious decision to have a type that just cannot be assigned to. Then and only then, just have a const member without any indirection.
Yes, you can
If you have a const data member, the copy and move constructor are deleted. But this only applies if the pointer itself is const, not the pointed value.
const & or const * are only fine when the class is uncopyable (like a "manager class" of some sort)
Related
I understand that std::unique_ptr is the way it is and probably won't be changed to break backwards compatibility but I was wondering if anyone has a good reason why the writers of the spec didn't overload the get method with a const variant that looks like
const T* get() const;
to follow the intent of the unique_ptr being const.
My best guess is that it is trying to mirror pointers and act like a T* const instead of a typical class. As a follow-up question, if I wanted to hold a pointer in a const-like fashion in a const instance of my class, should I be using something else other than std::unique_ptr to hold the data?
Update
In my case I want to protect myself from misusing the pointer in the class itself. I was writing a const move constructor MyClass(const MyClass&& other) and was copying the data from the new instance into other via std::copy. It took a long time to track down the bug because I had assumed the copy must be correct because of const protection. I'm trying to figure out what I could have done to protect myself from this outside of providing a const getter and using that within the class when doing the copy.
Smart pointers are pretending to be a raw pointer.
If you have class member which is raw pointer and use it in const method that you can't update a pointer, but you can modify object which is pointed.
Same behavior is desired for smart pointer. So std::unique_ptr::get is a const method, but doesn't force to return pointer to const object.
Note also that you can have a pointer to const object.
MyClass *pointerToObject
std::unique_ptr<MyClass> smartPointerToObject;
// but you can have also a case
const MyClass *pointerToConstObject
std::unique_ptr<const MyClass> smartPointerToConstObject;
In last case std::unique_ptr::get will return something you are expecting.
Based on comment below:
Just provide private methods:
InnerClass& GetField() { return *uniquePtrToInnerClass; }
const InnerClass& GetField() const { return *uniquePtrToInnerClass; }
And use it in your code and you will have const object of inner class in const method.
There's no point to giving read-only access to an object via its unique_ptr. You only pass unique_ptr around when you are transferring ownership, for access to the object without an ownership transfer, call up.get() and pass a const T* to the function that should only read (or if the pointer is never nullptr, it's also reasonable to evaluate *(up.get()) and pass a const T&).
As a bonus, this allows you to use that function with objects stored on the stack, embedded inside another object, or managed with a smart pointer other than unique_ptr.
There's a good discussion of all the unique_ptr parameter passing cases (in/out, const/non-const, etc) here:
How do I pass a unique_ptr argument to a constructor or a function?
For the same reason a T*const when dereferenced is a T&, not a T const&.
Constness of pointer is distinct from pointness of pointed-to.
get is const, it does not modify the state of unique_ptr.
Its constness does not impact the constness of the contents.
There is the idea of smart pointers that propogate constness, but unique_ptr is not that beast.
std::experimental::propogate_const wraps a pointer-like object and makes const travel through it.
It, or something like it, may solve your problem.
Note that I find half the time when I try to have const propogate like this, I discover I was wrong. But this may not be the case here.
In general, the proper way to handle the guts of a T*const in a const manner is to pass a T const& (or the nullable variant T const*).
I think this concern is valid,
there should be 2 versions for each de-referencing functions,
e.g.
const T* get() const;
T* get();
enter code here
I know that purpose of providing "T* get() const" is to ease replace existing raw pointer usages.
But since uniq ptr denotes ownership, it is incorrect that some one being able to modify some thing OWNED by the object via a immutable(const) reference [assuming modifying something fully owned by a object is same as modifying the object itself - which is true if this was an object instead of a ptr].
May be best option would be std to provide another version of Uniq ptr which holds to above idiom (only other option may be to derive a new class from uniq ptr and provide 2 versions for de-referencing )
Because as far as the unique_ptr is concerned, getting the internal raw pointer reference is a const operation. Calling .get() and retrieving the internal raw pointer of a std::unique_ptr does not change the internal state of the std::unique_ptr object itself. So it seems the library designers elected to mark it const without attention to what could happen to the underlying object if they just return a straight non-const reference to it.
In fact, if you have a std::unique_ptr inside an object, and you call a const member function of that object, you can still call non-const member functions on the internal std::unique_ptr inside that object. For example:
struct A {
void Const() const { }
void nonConst() { }
};
struct B {
std::unique_ptr<A> a;
void go() const {
a->nonConst(); // OK
}
};
Although you cannot perform non-const operations on the internal state variables of an object from one of its const member function, there is no rule that says you cannot perform non-const operations on other objects.
What you may be expecting is the constness promise to carry over from the unique_ptr to also apply to access to what it internally points to, so you'd expect unique_ptr to be written something like this:
template <typename T>
class cunique_ptr {
T* ptr;
public:
cunique_ptr() {
ptr = new T;
}
~cunique_ptr() {
delete ptr;
}
// You can only get a non-const pointer to the internals from a non-const object
T* get() { return ptr; }
// The const member function carries over the const promise to access to its internals
const T* get() const { return ptr; }
};
void test() {
cunique_ptr<A> a;
a.get()->nonConst();
const cunique_ptr<A> ca;
//ca.get()->nonConst(); //X fails: cannot call non-const member functions from const object
ca.get()->Const();
}
However, it seems the library designers elected against that type of protection and let the const promise be kind of shallow as it were.
I would like to ask a question about methods' const-correctness. Let me illustrate the situation.
class MyClass
{
public:
...
void DiscussedMethod() { otherClass->NonConstMethod(); }
private:
OtherClass *otherClass;
};
I have a class MyClass which keeps a pointer to OtherClass. In DiscussedMethod it calls OtherClass::NonConstMethod which modifies some visible data.
I would like to know, whether it would be a good practice to make the DiscussedMethod const (since it doesn't modify any member data)? Would it be a bad practice? Or is both fine?
What if the OtherClass kept a pointer to the MyClass and in NonConstMethod modified some of the MyClass' data (meaning that the MyClass member data would change during the DiscussedMethod call). Would it be a bad practice to make the DiscussedMethod const then?
As far as I've been able to find out, the const on a method is mostly a code documenting thing, so I would probably lean toward to not making the DiscussedMethod const, but I would like to hear your opinions.
EDIT: Some replies take the into account whether the object pointed to by otherClass is owned by the MyClass object. This is not the case in the scenario I'm working with. Lets say that both objects exist independently side by side (with the ability to modify each other). I think this analogy describes my situation quite well.
For example consider something like doubly-linked list, where each element is a class that keeps pointer to its neighbours and member variable color. And it has method MakeNeighboursRed which changes the color of its neighbours but doesn't affect the calling object's state itself. Should I consider making this method const?
And what if there was some possibility that MakeNeighboursRed would call neighbour's MakeNeighboursRed. So in the end the state of the object for which MakeNeighboursRed has been called originally would change as well.
And I would like to thank you all for your opinions :-)
If MyClass owns the OtherClass instance i wouldn't make DiscussedMethod constant.
The same goes for classes, managing resources. I.e. the standard containers do not return non const references or pointers to the managed memory using const functions, although it would be "possible" (since the actual pointer holding the resource is not modified).
Consider
class MyClass
{
public:
bool a() const { return otherClass->SomeMethod(); }
void b() const { otherClass->NonConstMethod(); }
private:
OtherClass *otherClass;
};
void foo (MyClass const &x)
{
cout << boolalpha << x.a() << endl;
x.b(); // possible if b is a const function
cout << boolalpha << x.a() << endl;
}
The foo could print two different values although an implementor of foo would probably expect that two function calls on a const object will have the same behaviour.
For clarification:
The following is invalid according to the standard since the const version of operator[] returns std::vector<T>::const_reference which is a constant reference to the value type.
std::vector<int> const a = { /* ... */ };
a[0] = 23; // impossible, the content is part of the state of a
It would be possible if there was only one signature of this function, namely referece operator[] (size_t i) const;, since the operation does not alter the internal pointers of the vector but the memory they point to.
But the memory, managed by the vector is considered to be part of the vectors state and thus modification is impossible through the const vector interface.
If the vector contains pointers, those pointer will still be unmodifiable through the public const vector interface, although the pointers stored in the vector may well be non const and it may well be possible to alter the memory they point to.
std::vector<int*> const b = { /* ... */ };
int x(2);
b[0] = &x; // impossible, b is const
*b[0] = x; // possible since value_type is int* not int const *
In OOP object should be fully described by its state, available through its interface. Thus, const methods should not alter object's state, if these changes might be observed through the interface.
A good example is a mutable mutex inside your class to guard some shared resources. It might be modified from const method, since it does not introduce any changes observable via class interface.
General rule of thumb is, that if you can make a member function const, you probably should. The reason for that is that it allows you to catch unintended behaviour and bug easier.
Another argument in favor would be that if you have this function as const you are allowed to call it on const object, so it isn't really a documentation thing.
Overall it depends what the other class is. It's not black and white...
If otherClass is a log object (for example) and you want to log the operation of the current object then it's perfectly fine calling it from a const function.
If the otherClass is a container that for design (or implementation) purposes is implemented as a separate object than effectively a const function modifies the object making this a very bad idea.
I hope this helps.
It's totaly incorrect to make DiscussedMethod const as it changes it's *this state. The only loophole to this is making non-logically-part-of-object's-state member data mutable so they can be changed in const functions. This would be things like a member that hold a count for "number of times function x() has been called". Any thing else is part of the object's state, and if a function changes it (at any level), that function isn't const.
I would like to know, whether it would be a good practice to make the DiscussedMethod const (since it doesn't modify any member data)?
otherClass is member data, and it (or rather, the object it points to) gets modified.
Consider the semantics should the pointer to otherClass be refactored to a fully-owned object... whether something is held as a pointer, reference, or object doesn't change the semantical ownership, IMO.
I have a member function which is declared const and modifies data via a pointer. This seems misleading. Should I remove the const keyword?
I would like to know how others handle this situation in their code. Do people just add a comment to clarify what is going on? Do they not add the const keyword to the member function? Maybe something else completely?
Any advice is appreciated.
You have essentially two choices:
Deep constness:
class Foo
{
T * ptr;
public:
T & operator*() { return *ptr; }
T const & operator*() const { return *ptr; }
T * operator->() { return ptr; }
T const * operator->() const { return ptr; }
};
Shallow constness:
class Foo
{
T * ptr;
public:
T & operator*() const { return *ptr; }
T * operator->() const { return ptr; }
};
It's really up to you, and to the purpose of your class. If the class is a smart pointer, it would seem reasonable to have shallow constness semantics, since the class is supposed to be as similar to a raw pointer as possible (and you can of course have a constant raw pointer to a non-constant pointee).
Otherwise, you should ask yourself why you would be exposing access to a member pointer object at all. It's certainly possible that you want to give mutable access via constant references to your class, but I imagine those are special and rare circumstances. There shouldn't really be that many raw pointers in your code in the first place. Returning a deeply-const reference by dereferencing a pointer should be fine, but usually in better encapsulated "getter" functions which hide the fact that there is a pointer inside your class, like T const & get() const { return *ptr; }.
Generally, yes. Its deceptive to modify something you are declaring constant, even though you can do it.
If someone uses your code and sees const, they expect const. Modification, even though sensible to you, might cause them severe problems -- even crashing a program.
Consider a std::vector<Blah> member versus a Blah* member used to implement a dynamic array. Most often it makes sense to replace the latter with the former. With the Blah* memeber a const method is allowed to modify the data in the array, while with the std::vector<Blah> member the const method is not allowed to modify data there.
Also consider a matrix class with an indexing method that returns a proxy that allows assignment to an element. Assigning via the proxy changes the matrix, not the proxy object itself. Thus, the proxy object’s assignment operator can be (and should be) const, in order to impose the most constraints possible on its effect, while its primary job is to modify things.
That’s another example that the design level is different from the coding level.
In the first example, with a member array, const was all about expressing a design level constraint, but in the second example, with the assignment proxy, const was all about expressing a coding level constraint.
These usages are not incompatible, however. The key idea is to provide a reader of the code with as many constraints as possible (because that greatly reduces how many varying things that must be considered to understand or deal with the code). Upshot: add const wherever you practically can.
C++ methods allow a const qualifier to indicate that the object is not changed by the method. But what does that mean? Eg. if the instance variables are pointers, does it mean that the pointers are not changed, or also that the memory to which they point is not changed?
Concretely, here is a minimal example class
class myclass {
int * data;
myclass() {
data = new int[10];
}
~myclass() {
delete [] data;
}
void set(const int index) const {
data[index] = 1;
}
};
Does the method set correctly qualify as const? It does not change the member variable data, but it sure does change the content of the array.
What can a 'const' method change?
Without explicitly casting away constness, a const member function can change:
mutable data members, and
any data the class has non-const access to, irrespective of whether that data's accessible:
via member variables that are pointers or references,
via pointers or references passed as function arguments,
via pointers or references returned by functions,
directly in the namespace or class (for static members) containing it.
These restrictions apply to operations of data members and bases (in the OO sense) too. More explicitly, a const member function operating on *this's data members or bases, when they're of class/struct/union type, can only call their const member functions (if any), and can only write to their mutable data members (if any).
(A const member function can also change any non-const local variables and by-value parameters, but I know that's not what you're interested in).
const data members can call other const member functions, which will have these same abilities and restrictions.
Eg. if the instance variables are pointers, does it mean that the pointers are not changed, or also that the memory to which they point is not changed?
It means the pointers can't be (easily/accidentally) changed. It does not mean that the pointed-to memory can't be changed.
What you've stumbled on is the logical incorrectness of a const function changing pointed-to or referenced data conceptually owned by the object. As you've found, the compiler doesn't enforce the const correctness you may want or expect here. That's a bit dangerous, but means constness doesn't need to be explicitly removed for pointers/references to other objects which may be changed as a side-effect of the const function. For example, a logging object. (Typically, such objects are not logically "owned" by the object whose const function is operating on them.) The key point is that the compiler can't reliably distinguish the type of logical ownership an object has over pointed-to data, so it's got to guess one way or the other and allow the programmer to either override, or not be protected by const-ness. C++ forgoes the protection.
Interesting, I've heard Walter Bright's D language flips this default, making pointed-to data const by default in const functions. That seems safer to me, though it's hard to imagine how often one would end up needing to explicitly cast away constness to allow wanted side-effects, and whether that would feel satisfyingly precise or annoyingly verbose.
Most succinctly, it means that the type of this is const T * inside const member functions, where T is your class, while in unqualified functions it is T *.
Your method set does not change data, so it can be qualified as const. In other words, myclass::data is accessed as this->data and is of type int * const.
There are two aspects to this question:
what does const mean to the compiler?
how does const apply when it cannot be validated by the compiler?
Question 1
The first is rather simple. The compiler validates that no data members are modified (unless they are qualified as mutable). It validates this recursively: for any user-defined types, it checks that no non-const methods are invoked. For built-in types, it validates that they are not assigned.
The transformation for pointers is T* to T*const (const pointer), not const T* (pointer to const). This means that the compiler does not validate that the object pointed to is not modified. Obviously, this leads to question 2.
Question 2
How does const apply when not validate by the compiler? It means whatever it should mean to your application. This is usually referred to as logical const. When to use const with respect to logical const-ness is subject to debate.
const when applied to a method means:
This means that the state of the object will not be changed by the method.
This means any members that are part of the objects state can not be modified, nor can any functions that are not also const be called.
As this relates to pointers. It means the pointer (if it is part of the state) can not be changed. But the object the pointer points at is part of another object so that means you can call non cost methods on this object (as it is not part of the state of this object).
const basically prevents changing the class instance members' values inside the function. This is useful for more clearer interface, but pose restrictions when using inheritance for example. It's sometimes a little bit deceiving (or a lot actually), as in the example you posted.
const would be most appropriate for Get functions, where it is obvious that the caller is reading a value and has no intentions of changing the object state. In this case you would want to limit the inherited implementations as well to adhere to constness, to avoid confusion and hidden bugs when using polymorphism.
For example
class A{
int i;
public:
virtual int GetI() {return i;};
}
class B : public A{
public:
int GetI() { i = i*2; return i;}; // undesirable
}
changing A to:
virtual int GetI() const {return i;};
solves the problem.
In RAII, resources are not initialized until they are accessed. However, many access methods are declared constant. I need to call a mutable (non-const) function to initialize a data member.
Example: Loading from a data base
struct MyClass
{
int get_value(void) const;
private:
void load_from_database(void); // Loads the data member from database.
int m_value;
};
int
MyClass ::
get_value(void) const
{
static bool value_initialized(false);
if (!value_initialized)
{
// The compiler complains about this call because
// the method is non-const and called from a const
// method.
load_from_database();
}
return m_value;
}
My primitive solution is to declare the data member as mutable. I would rather not do this, because it suggests that other methods can change the member.
How would I cast the load_from_database() statement to get rid of the compiler errors?
This is not RAII. In RAII you would initialize it in the constructor, which would solve your problems.
So, what you are using here is Lazy. Be it lazy initialization or lazy computation.
If you don't use mutable, you are in for a world of hurt.
Of course you could use a const_cast, but what if someone does:
static const MyClass Examplar;
And the compiler decides it is a good candidate for Read-Only memory ? Well, in this case the effects of the const_cast are undefined. At best, nothing happens.
If you still wish to pursue the const_cast route, do it as R Samuel Klatchko do.
If you thought over and think there is likely a better alternative, you can decide to wrap your variable. If it was in class of its own, with only 3 methods: get, set and load_from_database, then you would not worry about it being mutable.
You are basically implementing a caching mechanism. Personally I think it's OK to mark cached data as mutable.
As Matthieu already pointed out, what you're trying to do here has little (if anything) to do with RAII. Likewise, I doubt that any combination of const and mutable is really going to help. const and mutable modify the type, and apply equally to all access to an object of that type.
What you seem to want is for a small amount of code to have write access, and anything else only read access to the value. Given the basic design of C++ (and most similar languages), the right way to do that is to move the variable into a class of its own, with the small amount of code that needs write access as part of (or possibly a friend of) that class. The rest of the world is given its read-only access via the class' interface (i.e., a member function that retrieves the value).
The (presumably stripped down) MyClass you've posted is pretty close to right -- you just need to use that by itself, instead of as part of a larger class with lots of other members. The main things to change would be 1) the name from MyClass to something like lazy_int, and 2) (at least by my preference) get_value() should probably be renamed to operator int(). Yes, m_value will probably need to be mutable, but this doesn't allow other code to write the value, simply because other code doesn't have access to the value itself at all.
Then you embed an object of that type into your larger class. The code in that outer class can treat it as an int (on a read-only basis) thanks to its operator int(), but can't write it, simply because the class doesn't give any way to do so.
[ LOOK MA! NO CASTS! :)) ]
struct DBValue
{
int get_value();
private:
void load_from_database();
int value;
};
struct MyClass
{
MyClass(): db_value(new DBValue()) {}
~MyClass() { delete db_value; }
int get_value() const;
private:
DBValue * const db_value;
};
int MyClass::get_value() const
{
return db_value->get_value(); // calls void load_from_database() if needed
}
The idea is to have a politically correct MyClass with const methods not mutating anything but calling both const and non-const methods of aggregated objects via const pointers.
Don't use const_cast here, or you're asking for trouble. Using mutable in this case shouldn't be a problem, but if the profiler didn't suggest otherwise then I think users would be less surprised to see an object that is expensive to construct than an accessor method that is expensive to call the first time.
If your method changes the state of the object (e.g. by changing the state of the underlying database), then the method should not be const. In that case you should have a separate, non-const load-method, that has to be called before the const getter can be called.
This method would require neither const_cast not mutable, and would make the potentially expensive operation explicit.