Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly return reference to class member?

Tags:

c++

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?

like image 680
Dimitrios Menounos Avatar asked Nov 29 '09 14:11

Dimitrios Menounos


People also ask

How the returning by reference is performed?

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.

What does it mean to return a reference?

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.

How do you reference class members from within a class?

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.

Why we should avoid returning a reference to a data member of a class in a public member function?

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.


5 Answers

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;}
  // ...
};
like image 69
sbi Avatar answered Oct 01 '22 17:10

sbi


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.

like image 44
RC. Avatar answered Oct 01 '22 18:10

RC.


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.

like image 26
Lodle Avatar answered Oct 01 '22 18:10

Lodle


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;

EDIT: mutable defense

@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.

like image 26
KitsuneYMG Avatar answered Oct 01 '22 17:10

KitsuneYMG


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.

like image 26
P Shved Avatar answered Oct 01 '22 17:10

P Shved