Should I const_cast "this" when a method only accepts Foo *const? - c++

I have a class Foo which is in a self referencing tree-like structure (minimally):
class Foo {
public:
// Gets this child's position relative to it's parent.
int getPosition() const {
return parent->indexOf(this);
}
int indexOf(const Foo *const child) const {
return children.indexOf(child); // this line causes an error.
}
private:
Foo *parent;
QList<Foo *> children;
}
The line return children.indexOf(child) expects const T &value to be passed as per the QList docs, this resolves to Foo *const &value for my scenario.
In order for my getPosition() method to call my own indexOf() method it is required to have a signature of const Foo *child at minimum in order to pass this from a const method. (Since this is const Foo *const).
My code will not compile however as const Foo *const child cannot be cast to Foo *const child for QList::indexOf. Neither of my methods modify the object state so they should be const (i.e. I don't want to un-cost getPosition to receive a non-const this).
So the question is, how do I go from this in a const context (const Foo *const) to what QList::indexOf requires. Should I be const casting this inside getPosition since I know that my indexOf (and subsequent calls) will not mutate it?
Is there something else I should have done? Perhaps my design is faulty.

I think this is a perfectly reasonable use case for const_cast, however it's not this you need to const_cast, but child. In this case, QList::indexOf expects a constant pointer to a Foo (Foo* const) but child is a constant pointer to a constant Foo (const Foo* const). There is no implicit conversion from Foo* const to const Foo* const because that would remove the const-ness from the value being pointed to.
So, to fix your code I would change the line to
return children.indexOf(const_cast<Foo*>(child));
You know QList::indexOf is not going to modify whatever child points to, so this won't produce undefined behavior. I would however add a comment explaining why the const_cast is necessary.

Related

Why this syntax allowed in const function [duplicate]

Today I found out that code like that works. That sounds really strange to me, because as far as I always knew you can't modify any of members from const member function. You actually can't do it directly, but you can call non-const member function. if you mark member function as const that means that this pointer passed to the function is pointing to const object, then how non-const member function is called in the example bellow?
#include <iostream>
class X
{
public:
void foo() const
{
ptr->bar();
}
void bar() {}
private:
X * ptr;
};
int main()
{
}
Your member variable is not X, but pointer to X. As long as foo does not modify the pointer, it can be const.
When you have a pointer member, then the pointer is const in a const method. You won't be allowed to change the address stored in the pointer. But you can change the pointee all you like.
It's the difference between
X* const cannot_change_pointer; //this is how top-level const applies to pointers
const X* cannot_change_pointee;
What's even more interesting is that const on a method has no effect whatsoever on reference members for the same reason (a const method would only prevent you making a reference refer to something else which can't be done with a reference anyway).
That's seems perfectly OK. The call to foo does not modify the ptr member. Hence, the constness of foo is respected.
my 2 cents

Allowing a function to mutate a const object's member variable

This is related to the (currently) closed question I asked earlier: Can you mutate an object of custom type when it's declared as constant?
Suppose we have something that looks like the following:
class test
{
public:
test() : i{4}, ptr{&i} {};
int i;
int *ptr;
int *get_ptr() const {return ptr;}
};
void func(const test &t, int j) {
*(t.get_ptr()) = j;
// auto ptr = t.get_ptr();
// *ptr = j;
}
int main(int argc, char const *argv[])
{
test t;
std::cout << t.i << std::endl;
func(t, 5);
std::cout << t.i << std::endl;
}
We have this func that takes in a const test &. When I see this signature (and if I didn't look at the implementation of the function), it makes me want to assume that nothing in t will get modified; however, the member variable i is able to be modified through the ptr member variable, as we see here.
I don't usually write code that end up working this way, so I'm wondering if code like this is discouraged?
Furthermore, is it reasonable to assume (most of the time) that an object declared as const will not be mutated?
Yes code like this is definitely discouraged. It completely ignores the reason we have the const keyword in the first place. The function is deliberately modifying something that it is advertising it will not modify
That means that whoever wrote the get_ptr() function messed up because they declared it const but let it return a pointer to non-const object (so one that can be changed, defeating the purpose of declaring the function const)
If whatever get_ptr() returns could properly be modified by such a function then it should be an implementation detail, a private (or protected) variable marked with the mutable keyword.
test::get_ptr() should have two overloads.
const int* get_ptr() const { return ptr; }
int* get_ptr() { return ptr; }
If func() wants to change the test object given to it then it should take test&, not const test&
The key is here:
int *get_ptr() const {return ptr;}
You define get_ptr as const which is akin to saying "get_ptr is not going to change any attributes of the class". And it doesn't. It returns the value of the attribute ptr, which is a int*, and points to a mutable instance of an int which happens to be an attribute of the class as well.
The compiler has no way of knowing this, so from the compiler's perspective the promise was kept. However in reality you're circumventing the const qualifier and allowing to mutate an otherwise immutable attribute.
Not the best of coding practices, but whoever writes such code should, obviously, not expect for i to remain as it was set in the class methods if get_ptr is ever called.

const keyword and duplicate method signature

I just started learning C++, switching from a JAVA environment.
When reading through some Boost examples I found the following two methods defined in a class:
const char* data() const
{
return data_;
}
char* data()
{
return data_;
}
There are two things that confuse me.
First is the reserved word const, which I think I understand here. The first const refers to the char* which means that I cannot change the value of the pointer. The second const tells me that calling the function will not make changes to the state of the object I am calling. Is that a correct interpretation?
Second point of confusion is why one would have two methods with the same name and signature. How does the compiler know which one I meant to call? How do I know whether I am allowed to change the data after calling data() without know which of the two I called?
The first function returns a pointer to constant data. The const at the end of the function signature indicates that the function will not modify and class data members.
The second function returns a pointer to mutable data. The caller can use the pointer to modify the class member variable.
Search the web and SO for "const correctness".
There are several ways to declare const within a variable with a pointer:
const char * // you can modify the pointer, not the item pointed to
char const * // you can modify the pointer, not the item pointed to
char * const // you can modify the item pointed to, not the pointer
const char const * // you cannot modify either
char const * const // you cannot modify either
const char * const // you cannot modify either
As for the question, these two method definitions are overloads, and which one is called depends on the context. For example, if the caller is also in a const method, and making the call on one of it's members (who is an instantiation of the class that has the data() methods), then the const char * data() const method will be called, and caller can only save the return value in a variable of type const char *. Here is an example:
class MyClass {
DataClass data_obj_; // has both 'data()' methods described in your question
...
void my_method () const { // within this const method, data_obj_ cannot be modified
const char * data = data_obj_.data(); // calls const method
...
Here is an example where you would need both.
class foo {
char* data_;
public:
const char* data() const
{
return data_;
}
char* data()
{
return data_;
}
};
const foo bar;
foo bar2;
bar->data(); // this uses the const version.
bar2->data(); // this uses the non-const version.
Because bar is a const object it would be an error to call data() non-const version.

Can const member function return a non-const pointer to a data member?

Code goes first:
class A
{
public:
...
int *foo() const
{
return _px;
}
private:
int *_px;
}
The member function foo returns a non-const pointer to private member _px, which, I think, opens a door to modifying member _px, right?
Is foo a const member function? Should I add a const in front of the return type?
UPDATE
What a const-member-function should guarantee is that, it cannot change any data-member, right?
In my case, function foo doesn't open a door to modifying class As data-member _px, but a door to modifying what _px pointing to, So my question is, does this violate what a const-function should guarantee?
A const member function can only return a const pointer or reference to a member.
However, your example isn't returning a pointer to a member; it's returning a copy of a member that happens to be a pointer. That is allowed in a const member function (even if the pointer happens to point to another member).
This would not be allowed (note that it's now returning a reference):
int *& foo() const {return _px;}
but this would (returning a const reference):
int * const & foo() const {return _px;}
int *_px becomes int *const _px inside a const member function this implies that the pointer cannot be reseated but the data pointed to is still modifyable. Further your function returns a copy of the pointer so it does not matter anyways.
It does not open a door to modifying _px but rather what _px points to. It's up to you to decide whether you want to allow this or not.
For example, an iterator::operator-> would return a non-const pointer and const_iterator::operator-> would return a const pointer. Both methods can be const themselves.
Yes, for your case it can. However, it's generally advised to not to do this, because it allows changing constant objects:
void f(const A& a)
{
*(a.foo()) = 42; // damn!
}
Yes, for example see the std::streambuf pointers:
protected:
char* pbase() const;
char* pptr() const;
char* epptr() const;
http://en.cppreference.com/w/cpp/io/basic_streambuf/pptr

Const Methods that Return References

class Foo
{
int Bar;
public:
int& GetBar() const
{
return Bar;
}
}
Is it okay that GetBar is a const method? It's not actually changing anything, but it's providing the "outside world" with a means of changing it.
You have a typo in your code, this is what you probably meant:
class Foo
{
int Bar;
public:
int& GetBar() const
{
return Bar; // Removed the ampersand, because a reference is returned, not an address
}
}
And no, this is not legal. When tagging a method with const, not only are you promising you won't touch any of the internal state of the object, you also promise that you will not return anything that can be used to change the state of the object. A non-const reference may be used to modify the value of Bar outside the scope of GetBar(), hence you are implying that you cannot hold the promise.
You must either change the method to being non-const, return a const reference, or make Bar exempt of the promise by marking it as mutable. E.g.: mutable int Bar; The mutable keyword tells the compiler that the logical constness of the object does not depend on the state of Bar. Then you are allowed to do whatever you please with it.
Nope, since you cant do the following assignment:
const int x; int &y = x;
What you can do though is const int x; const int &y = x;
And of course there is no problem in overloading the method and creating both const and non-const variants.
You probably want to return Bar, not &Bar. Anyway, I dropped this code in Comeau:
class Foo
{
int Bar;
public:
int& GetBar() const
{
return &Bar;
}
};
int main(int argc, char** argv)
{
Foo x;
int y = x.GetBar();
y = 5;
return 0;
}
And got the error:
line 9: error: qualifiers dropped in binding reference of type
"int &" to initializer of type "const int"
return Bar;
^
First of all, to return a reference you don't need to use the ampersand operator on the referenced object; it is needed if you want to get a pointer to it. So, I assume you wanted to write return Bar;.
Then, no, you can't do that; in a const method you have a const this pointer (in your case it would be a const Foo *), which means that any reference you can get to its fields1 will be a const reference, since you're accessing them through a "const path".
Thus, if you try to do what you did in that code you'll get a compilation error, since you'd be trying to initialize an int & (the return value of your method) with a const int & (the reference you get from Bar), which obviously is forbidden.
g++ actually says:
testconstref.cpp: In member function ‘int& Foo::GetBar() const’:
testconstref.cpp:9: error: invalid initialization of non-const reference of type ‘int&’ from a temporary of type ‘const int*’
which is what I just said. :)
If, instead, you return a const reference to a class field from a const method you'll have no problem.
Excluding fields marked as mutable, which tells to the compiler that such fields are modifiable even from const methods; this exception has been introduced to allow const methods to change the "real" state of the object in cases when this do not alter its "logical" state; this can be useful to implement lazy evaluation, reference counting, ...
The members of your class are considered const when you are in a const method. So although Bar is int within your class and not const int, in the context of GetBar() const it is "const int". Therefore returning it as a non-const reference or pointer is just as illegal as doing:
const int y = 57;
int& z = y;
This breaks const-correctness even though the 2nd line has not actually changed anything (yet) in y.
Note incidentally that if your class has pointer members the only thing that is const is the pointers themselves and not what they point to. (Often referred to as "shallow" constness)
Thus this would be legal:
class A
{
Foo * foo;
public:
Foo * getFoo() const // legal. does not have to return const Foo *
{
return foo;
}
};
Note that in your original code you would be allowed to return Bar by non-const reference if it were mutable, because those members are not bound by the constness of member functions.
const modifier to a member function doesn't allow to change the object's state with in it's scope. Compiler just checks whether this function is modifying the state of the object or not in its scope. Taking another example -
class foo
{
int num ;
public :
foo( int anum )
{
anum = 10;
}
int getNum()
{
return num;
}
};
foo obj;
int& myNum = obj.getNum() ; // myNum is just an alias to the private class variable num
myNum = 40; // Actually changes the content of the private variable.
So, compiler just checks access specifiers ( i.e., whether this variable is accessible or not in this scope) but not about the private/public/protected variable's memory location if returned to some other variable.