Why does the const Object returned by QList::at() block access to instance methods?



  • Hi,

    This is probably somewhat a general C++ question but it does pertain to the use of QList.

    I have a class (I'll say CustomClassA) that contains a member QList<CustomClassB> mListofB, which has a corresponding getter "getBList()" that simply returns mListofB. CustomClassB is a child of CustomClassA

    I know at() returns a constant reference to the selected object in this list and is the fastest access method, while value() returns a non-constant instance, is slightly slower, and generates a instance of the class contained in the list of the provided index is out of bounds. I prefer to use at() to ensure I get runtime exceptions if there is an out of bounds issue.

    My issue is this following:

    I have a pointer to CustomClassA in MainWindow (though this occurs without the use of a pointer) called mBSCPtr. At some point I need to read a member variable within CustomClassB that is part of the mListofB list in my CustomClassA instance. So naturally I try this:

    mClassAInstance->getBList().at(i).getterWithinClassB();
    

    but after I finish typing "at(i)." the auto-completer doesn't show any of the functions within ClassB and only lets me access static constant variables within that class. If I manually complete the statement as above, there I get the following error:

    error: 'this' argument to member function 'getterWithinClassB()' has type 'const CustomClassA::CustomClassB', but function is not marked const
    

    if I do this instead:

    CustomClassB tempBInstance = mClassAInstance->getBList().at(i);
    int desiredValue = tempBInstance.getterWithinClassB();
    

    it works fine. This is alright, except that now I have created a copy of that QList<CustomClassB> that I don't want (unnecessary memory usage) since I just want a copy of one value that is a member within that list.

    I can also compile:

    mClassAInstance->getBList().value(i).getterWithinClassB();
    

    with no problems, but like I said I'd prefer to use .at() to enforce proper bounds and if I had to guess I'd say that value() temporarily makes a copy of the list entry before it hands off the value I actually want and then destroys that copy, which has the same issue as the above.

    Is there any way I can adapt this so that I am using "at()", not making unneeded copies, and am still able to access the instance functions of CustomClassB?

    I'm certain I'm being dumb and forgetting a fundamental in regards to const objects. Anyone care to refresh me?

    Qt 5.12.3 32bit Debug via MSVC
    Windows 10


  • Moderators

    @oblivioncth said in Why does the const Object returned by QList::at() block access to instance methods?:

    ....but function is not marked const
    

    Mark it const. MyReturnType CustomClassB::getterWithinClassB() const { ... }

    In general, all methods that don't modify the object should be marked const.



  • Oh wow,

    I'm embarrassed to say that I didn't even know a function could be marked as constant, other than marking its return object with 'const', which is why the error confused me a bit.

    Very useful, totally makes sense now, and obviously fixes my issue. Definitely a habit I'll pick up right away. Many complain about this, but man does C++ have a lot of different ways to use keywords.

    Thanks a bunch.


  • Moderators

    @oblivioncth No problem, this is how we learn and become experienced.

    Anyway, here's a pretty good article that explains all you need to know about consts: https://www.cprogramming.com/tutorial/const_correctness.html

    Happy coding!

    man does C++ have a lot of different ways to use keywords.

    You know it.

    As an exercise, make sure you understand the effect of each of the 4 consts below:

    MyClass {
    public:
        const int& myfunc(const MyOtherClass * const arg) const;
    };
    


  • Hey, going above and beyond! Thanks again.

    I'm not completely ignorant in the ways of C++ though, I'm not 100% certain, but fairly confident that:

    Const 1) Ensures the returned value can't be modified, the use cases of which I'll admit I'm not really privy to. I know that its only really useful when dealing with user defined classes and returning by value (which is the case in your example) to prevent memory modification if desired. I do remember seeing something about this being somewhat obsolete in C++ 11 (or was it 17) and forward, though I'm not sure why.

    Const 2) The pointer argument "arg" in that class is for the type "const MyOtherClass", i.e. a pointer to a constant value (value cannot)

    Const 3) Marks the pointer "arg" itself as pointer, so that the pointer itself also cannot be modified)

    Const 4) Thanks to you, I now understand this means that the function does not modify the object instance that the function is being called on/from (i.e. cannot manipulate member variables).

    Btw I do like a lot of the explanations on that site as they provide a lot of detail and examples, and I had never read the article for const.


  • Qt Champions 2019

    @oblivioncth said in Why does the const Object returned by QList::at() block access to instance methods?:

    when dealing with user defined classes and returning by value (which is the case in your example)

    It's not the case. Return value is here a reference. Const does not make any sense in case of return by value as a copy is returned and the original variable can't be changed this way. In case of a reference the original member variable can be changed if not marked as const.



  • @jsulm said in Why does the const Object returned by QList::at() block access to instance methods?:

    @oblivioncth said in Why does the const Object returned by QList::at() block access to instance methods?:

    when dealing with user defined classes and returning by value (which is the case in your example)

    It's not the case. Return value is here a reference. Const does not make any sense in case of return by value as a copy is returned and the original variable can't be changed this way. In case of a reference the original member variable can be changed if not marked as const.

    If you believe me that was actually just a typo. I meant to say return by reference (hence the "unless" since returning by value is typically the default choice), and as you said using a const on a return by value since that data cannot be modified anyway since you are only left with a copy in the end.


  • Moderators

    @oblivioncth said in Why does the const Object returned by QList::at() block access to instance methods?:

    Hey, going above and beyond! Thanks again.

    You're most welcome!

    I'm not completely ignorant in the ways of C++ though, I'm not 100% certain, but fairly confident that:

    Const 1) Ensures the returned value can't be modified,

    Yep.

    the use cases of which I'll admit I'm not really privy to.

    Use case 1:

    • Allow the caller to read the data without creating a copy, AND
    • Make sure the data is read-only

    If the returned reference is non-const, the caller will be able to directly modify the object's internal memory. This breaks encapsulation.

    Use case 2: Allow the function to be called in another const function.

    There's a const and a non-const version of QList::operator[](int):

    1. T & operator[](int i)
    2. const T & operator[](int i) const

    Version #1 allows the caller to modify the QList element. However, it cannot be called in a const function -- it will cause the error in your original post. Thus, version #2 is required for use in const functions.

    I know that its only really useful when dealing with user defined classes and returning by value reference (which is the case in your example) to prevent memory modification if desired.

    (Acknowledging your typo)

    Why would it be "only really useful when dealing with user defined classes"? Notice that QList::at() returns a const reference.

    I do remember seeing something about this being somewhat obsolete in C++ 11 (or was it 17) and forward, though I'm not sure why.

    Returning const references is defintiely not obsolete.

    You might be remembering move semantics, which is said to make passing parameters by const-reference obsolete: https://stackoverflow.com/questions/10231349/are-the-days-of-passing-const-stdstring-as-a-parameter-over

    Qt will not be making this change anytime soon though; this is too disruptive.

    Const 2) The pointer argument "arg" in that class is for the type "const MyOtherClass", i.e. a pointer to a constant value (value cannot)

    Yep, myfunc() cannot modify the MyOtherClass object. (In other words, it can only call const methods of the object)

    Const 3) Marks the pointer "arg" itself as pointer, so that the pointer itself also cannot be modified)

    Yep. This is not very useful IMHO, but it's part of the language.

    Const 4) Thanks to you, I now understand this means that the function does not modify the object instance that the function is being called on/from (i.e. cannot manipulate member variables).

    Great!

    Next, start thinking about the difference between logical const-ness and physical const-ness: https://isocpp.org/wiki/faq/const-correctness#logical-vs-physical-state

    Btw I do like a lot of the explanations on that site as they provide a lot of detail and examples, and I had never read the article for const.

    Agreed