Qt (C++) - method argument optimization?

  • I'm going back on my code after starting reading a C++ Book (programming-principles-and-practice-using-cpp-2nd)

    My background is Java so I mostly never used const and reference passing (&) in my functions

    Is there any benefit of using it with Qt?
    For example, passing a QList<Object> to a method this way:

    My original way (by value) :
    void method (QList<Object> lstObject)

    Recommended way (const reference):
    void method (const QList<Object>& lstObject)

    Is it worth it to also do it with basic type like int, double) ?
    What about Signal & Slot?

    I wonder why the compiler doesn't do this stuff for us, detect if we modify the method argument and optimize accordingly. I find the syntax to be a bother and hard to read.
    Thank you!

  • Moderators

    the most important advantage is simply performance.
    Since you pass a const-reference of a list it can be passed directly to the calling function. The const ensures that it is not altered by the callee.
    When you pass it by value the whole list needs to be copied before it is passed to the callee. Depending on the data type this can be a very fatal operation which slows down your application. For small lists with simply data-types you wont recognize a difference.
    But it's good practice to do so and a big advantage of C++ to give you more control of the memory.

  • Lifetime Qt Champion

    No need to use references for basic types.
    A reference is besides the scene a pointer, since an int has the same size as a pointer there is no benefit to pass int by const reference (other basic types have similar size, so no need for references).
    For signals/slots same rules apply.

    The compiler does not do it for you because you could change the parameter in your function/method by MISTAKE. So, it is up to you to tell the compiler whether you're going to change the parameter (non const reference) or not. Usually you should know what you are doing and write the code accordingly.

  • Moderators

    @jsulm said:

    No need to use references for basic types.

    dangerous advice for a C++ beginner.
    Imagine a list with a few hundrets of thousands int values contained. All values need to be traversed and copied in memory. And this is sub-optimal when it can be avoided.

    I forgot to mention that many classes are implicitly shared in Qt. So like the QList for example.
    Nevertheless the const-reference way is the recommended one in all cases.

  • Lifetime Qt Champion

    @raven-worx I was talking about basic types like int, double, char and so on not about complex types like lists.

  • Moderators

    ah k...got it after reading it a second time

  • Thanks for the tips guys,

    I will revisit my code and change to "const reference" where I don't modify the argument and the argument is something else than a basic type (int, double).

    I think jsulm meant basic type that are not in container, e.g (int and not QList<int>)
    Also what I meant for the compiler is to detect wheter we change the argument, if so, pass it by value. If not, pass it by reference. But of course C++ let you modify function argument, and this would not let you do that (not a bad thing IMO..)

  • Let say I have a constructor with a "const QVector<int>& vecId"
    My goal is that I can manipulate this list but not modify it. (read only).

    So I assign this list to a local QList of this Object.

    this.vecId = vecId;  // vecId is of type QVector<int> vecId2
    qDebug() << "THIS IS THE LENGTH BEFORE REMOVAL " << this->vecId .size();
    this->vecId .removeAt(0);
    qDebug() << "THIS IS THE LENGTH AFTER REMOVAL " << this->vecId .size();

    Here I can still modify the QList
    Is there a way to optimize this code or am I forced to pass the whole list by value like I was doing before? Thanks

    Interesting, maybe I don't need to do it

  • Moderators

    @maximus said:

    I wonder why the compiler doesn't do this stuff for us, detect if we modify the method argument and optimize accordingly

    Because it can't. You can pass that parameter into another function. It can get an address of it and modify that. It can const_cast the parameter. It can use a goto into a code that modifies it. It can do million other crazy stuff with it. Compiler can't make guesses. It simply needs to be perfectly deterministic. The only way this sort of automatic optimization was possible is if your whole program was a constexpr evaluated at compile time, but then there would be no point in optimizing it at all, and such program would be useless anyway.

    Here I can still modify the QList

    No. You can (and did) modify a copy of the list. The original is intact.

    Interesting, maybe I don't need to do it

    Implicit sharing is meant to help moving large structures around but it's not free! It is still a copy of the object. A copy constructor is called. An internal reference count is changed as an atomic operation. The data is not copied (initially) but still a lot is going on. A const reference is basically a pointer on the stack, or even in a cpu register, which is as close to "free" as you will get.

  • @Chris-Kawa

    I was thinking something like this method

    void printString(QString s) {
        qDebug() << s;
    The compiler could detect that nothing was assigned to "s" since it can read the whole function, so it replace the argument with const QString& s.  In other words, the value of "s" after function execution is the same value as before for any input possible.

    I will read more on C++. For now, I will only replace my getter functions and add const at the end of them. Hopefully I can get the benefit on implicit sharing without doing too much work.
    I just find it a bit sad the learning curve of C++ compared with other languages. That's what you get for starting with an "easy" language that doesn't have pointers and do memory management for you I guess. Thanks!

  • Moderators

    I was thinking something like this method

    You're forgetting that in c++ you can overload operators.
    The signature of << is QDebug & operator<<(const QString & s) and that to some extent helps.
    But what if it was QDebug & operator<<(QString& s)? How would the compiler know then that operator<< does not modify s internally?

    I said to some extent because the compiler is really fighting an uphill battle.
    For example the first line in that operator method could be something like

    QString& foo = const_cast<QString&>(s);
    foo = "gotcha!";

    If you think a compiler can follow this then what if the implementation was in a different translation unit or even in a different library? Of course the example is contrived and plain silly, but none the less valid c++ that a compiler must deal with correctly and not optimize stuff away.

  • Thanks Kawa, that example made me understood the whole problem.
    Since any method can modify it's argument in C++, we need to had const to explicitly state that it does not.
    I just never use this C++ feature (passing by reference without const), so that's probably why it was not natural for me.
    I will go back to the books! I just feel changing an argument is "wrong" and I use a return value to do it, old habits...

  • Moderators

    @maximus said:

    I just feel changing an argument is "wrong" and I use a return value to do it, old habits

    If possible you should definitely avoid non-const reference arguments. Returning a value instead of modifying an argument is actually a very good thing in many situations. The optimizer will like that a lot.

    Since you're up to reading about c++ you might want to look up return value optimization.
    The fastest way to copy is to avoid copying in the first place ;)

    Happy coding.

Log in to reply