class Foo {
protected:
QPoint& bar() const;
private:
QPoint m_bar;
};
QPoint& Foo::bar() const {
return m_bar;
}
I got this error:
error: invalid initialization of reference of type ‘QPoint&’ from expression of type ‘const QPoint’
However it works if I change it to this:
QPoint& Foo::bar() const {
return (QPoint&) m_bar;
}
1) I don't understand why the compiler says that my QPoint is const.
2) Is it ok to leave the cast there?
Returning values by reference in C++ A C++ function can return a reference in a similar way as it returns a pointer. When returning a reference, be careful that the object being referred to does not go out of scope. So it is not legal to return a reference to local var.
It means you return by reference, which is, at least in this case, probably not desired. It basically means the returned value is an alias to whatever you returned from the function. Unless it's a persistent object it's illegal.
The members of a class are referenced (accessed) by using the object of the class followed by the dot (membership) operator and the name of the member. The members of a class are referenced (accessed) by using the object of the class followed by the dot (membership) operator and the name of the member.
You should generally avoid returning references because it makes it crazy-hard to understand when constructors/destructors get called. However, returning a reference can be faster.
In a non-const member function of class Foo
the this
pointer is of the type Foo* const
- that is, the pointer is const, but not the instance it points to. In a const member function, however, the this
pointer is of the type const Foo* const
. This means that the object it points to is constant, too.
Therefore, in your example, when you use this->m_bar
(of which m_bar
is just the short form), then m_bar
is a member of a constant object - which is why you cannot return it as a non-const reference.
This actually makes sense from a design POV: If that Foo
object is a constant object, and you are allowed to invoke Foo::bar
for constant objects, then, if this would return a non-const reference to some internals which you can fiddle with, you would be able to change the state of a constant object.
Now you have to look at your design and ask yourself how you arrived at this point and what your actual intention is. If that m_bar
member isn't really part of the state of the object (for example, it's only there for debugging purposes), then you could consider making it mutable
. If it is part of the state of the object, then you have to ask yourself why you want to return a non-const reference to some internal data of a constant object. Either make the member function non-const, or return a const reference, or overload the member function:
class Foo {
public:
const QPoint& bar() const {return m_bar;}
QPoint& bar() {return m_bar;}
// ...
};
What you're trying to do is a no-no. You do not want to return a QPoint & from your bar function as this breaks encapsulation because a caller can now modify m_bar out from underneath QPoint. (luckily you have the function declared as const or you wouldn't get an error here and you would still be breaking encapsulation).
What you want in this case is
const QPoint &Foo::bar() const
{
return m_bar;
}
Edit based on user comment:
IMHO, this would be better solved by adding a setX
to Foo
instead of calling a non-const function from a non-const reference returned by an accessor function that should really be const. Overloading the accessor function to remove the const just puts a band-aid over the real problem and just hides the fact that encapsulation is compromised. Adding setX
to Foo fixes the encapsulation problem and also plugs the leaking interface you are creating by returning a non-const reference to private data.
For example, if you put foo.bar().setX(100);
in many parts of your application and later change the type of QPoint
to one that doesn't implement setX
or simply rename the function setX
to say setPointX
you have problems b/c you now have to go around a rename/refactor all of these calls. Creating a setX on Foo makes the code easier to call foo.setX(100)
vs foo.bar().setX(100)
, is const correct, and encapsulates your data. If you changed QPoint to be just 2 x and y coordinates in Foo, nothing outside of your class would have to change b/c you have good encapsulation.
The const on the function tells the compiler that the function doesnt modify any member variables. However, since your returning a reference to the member variable it cant guarantee that any more and thus has the compile error.
The compiler is not expecting your member variable to be const but the return from the function to be const. Remove the const from the function def.
either use
const QPoint& Foo::bar() const {
return m_bar;
}
or
QPoint& Foo::bar() {
return m_bar;
}
I guess you could also declare m_bar as:
mutable QPoint m_bar;
@tstenner:
mutable has its place. It is not 'evil' definitely no more so than void* or casting. Assume something like the following:
class foo {
SomeType bar;
public:
foo() : bar() { }
const SomeType& bar() const { return a; }
};
In this case, bar is ALWAYS constructed, even if bar() is never called. This may be fine, but if SomeType has a costly constructor, it can be preferable to allow lazy instantiation of bar.
Consider:
class foo2 {
mutable SomeType* bar;
mutable bool cached;
public:
foo2() : bar(0), cached(false) { }
const SomeType& bar() const {
if( ! cached ) {
cached = true;
bar = new SomeType();
}
return *bar;
}
};
This allows foo2 to mimic a non-lazy instantiated class, while being lazily instantiated. Granted, the mutability of foo2 has implications for thread safety, but these can be overcome with locking if needed.
If it allowed you to return a reference to the variable, then you could modify a const
instance of Foo
class. Consider such code:
void baz(const Foo &foo)
{
QPoint &ref = foo.bar(); //if it was allowed...
ref = 5; // ...you could have modified a const object foo!
}
Therefore compiler prevents you from doing that.
You should either declare your method returning const QPoint&
or to revise your understanding of what really const
is for.
Someone may advise you to use mutable
. Don't. mutable
keyword is to allow implementation of the class to modify internal member variables of const
objects (for example, for memoization purposes), rather than to expose non-const variable (!) to external code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With