C++ Weekly - Ep 447 - What Are Reference Qualified Members?

Поділитися
Вставка
  • Опубліковано 11 січ 2025

КОМЕНТАРІ • 40

  • @MattGodbolt
    @MattGodbolt 3 місяці тому +38

    I've used this on "builder" type objects where I build up by calling methods on some kind of factory thing (that maybe builds up vectors, unique_ptrs etc) and then to "build" I make it an `&&` r-value function that then can move all the configured pieces into a single, final object. Then one has to call `std::move(builder).build();` which makes it clear(ish) that the builder is now "finished"

    • @dreum99
      @dreum99 3 місяці тому

      I've played with similar concepts. The trick I used is that the "builder" is only available through a factory method and it's return in an "RvalueOnly" wrapper object. All the builder calls are forwards via an overloaded "operator->" that is an r-value function, so you're never allowed to get an lvalue version of the builder. This does make it so you have to chain all the calls together which you may not want, but it doesn't require a std::move, which feels unintuitive to me. Feel free to disagree though.

    • @japedr
      @japedr 3 місяці тому +3

      If I'm not mistaken, for one-off building you can achieve the same effect by constructing the builder in place, e.g. Builder{}.x(1).y(2).build(). (build() is called with an r-value reference of the builder object).

    • @AlfredoCorrea
      @AlfredoCorrea 3 місяці тому +3

      I think builder objects make sense if they are doing non-trivial work as they are build in steps (including checking arguments). Otherwise a skeleton object with just parameters is enough {.x = 1, .y = 2}. i could be missing something though. in any case, moving “at the end” is a great idea.

    • @bronekkozicki6356
      @bronekkozicki6356 3 місяці тому

      Thanks Matt, that's one interesting use case I did not foresee :D

  • @saulth
    @saulth 3 місяці тому +6

    I've used this on a tensor class for the assignment operator. The lvalue assignment is just the default of assigning a different tensor object to the value, but assignment to an rvalue meant broadcast assignment into the tensor, allowing a slice operator to return a temporary tensor that shared the original memory and then broadcast assignment into that temporary slice.

  • @VincentZalzal
    @VincentZalzal 3 місяці тому +2

    You can also decide to delete only one of the overloads. For example, if you want to forbid calling some member function on a temporary object, delete its rvalue-qualified version.

  • @N....
    @N.... 3 місяці тому +1

    Another benefit is that if you have a member function that would always result in undefined behavior if called like get_obj().get_value(), you can just add that single ampersand to the non-const version and/or add a deleted rvalue ref version for the const version. The compiler will then protect you from yourself.

  • @alskidan
    @alskidan 3 місяці тому +4

    I used reference qualified functions extensively to implement a builder DP. It allowed me to get rid of the explicit .build() at the end. Made everything much much more readable.

    • @alskidan
      @alskidan 3 місяці тому +3

      Oops, Matt wrote the same message before me. This is what one gets for not reading the comments. Well, never mind.

    • @dreum99
      @dreum99 3 місяці тому +2

      My builder also did the same thing. I'd be interested in seeing your implementation.

  • @friedkeenan
    @friedkeenan 3 місяці тому +5

    You can also have volatile-qualified member functions, isn't that super fun and awesome

  • @carlpittenger
    @carlpittenger 2 місяці тому

    first time i encountered/noticed this was the c++ weekly episode about all of the special assignment methods

    • @cppweekly
      @cppweekly  2 місяці тому

      oh yeah, I forgot about that one...

  • @KhalilEstell
    @KhalilEstell 3 місяці тому +6

    I... did not know that this existed. Neat! Good to know.

    • @cactus-man
      @cactus-man 3 місяці тому

      Same... And I've been using C++ professionally for ~3 years now. Guess it's time to go back to basics and see what else I missed...

  • @Pesquisando0b1011
    @Pesquisando0b1011 3 місяці тому +1

    Hi Jason. C++ templates usually produce the worst error messages. Could you make a series video dealing with this case of debugging template codes?

    • @cppweekly
      @cppweekly  3 місяці тому

      I'm always happy to have topic requests. I track them here so I don't lose them: github.com/lefticus/cpp_weekly/issues/ Feel free to add yours to the list and vote on the other suggestions.

  • @wissensfrosch
    @wissensfrosch 3 місяці тому +1

    I think the main problem to be solved here is that getters often return an l-value reference to the member for performance reasons or in the case of a non-const member to have a non-const l-value reference to be able to change the member (one special form of getters is the index operator). The problem now occurs if you do this on a temporary object. Overloading the member function for r-values helps to prevent this. (I believe that by now a reference to a member extends the lifetime of the temporary object. Then, much of what I said is not an issue anymore.)

  • @simonmaracine4721
    @simonmaracine4721 3 місяці тому +1

    Cool stuff!

  • @VladykaVladykov
    @VladykaVladykov 3 місяці тому +2

    Всегда рассказываю об этом студентам, не понимаю, почему об этом так мало говорят! Наверное, так же редко, как о указателях на член класса, типа `int Car::* pSpeed = &Car::speed;`

    • @cactus-man
      @cactus-man 3 місяці тому

      As a C++ dev you must know that explicitness is the most important thing there is.
      Your comment seems pretty comprehensible after translation, so why don't you just use any translator available to you (if you're too lazy to translate your comment yourself) to provide an explicit translation of your thoughts instead of relying on implicit auto-translated option?

    • @VladykaVladykov
      @VladykaVladykov 3 місяці тому +1

      @@cactus-man
      Thank you for your input! I didn't intend to cause any confusion. I’m just used to my browser automatically translating content, so I thought anyone interested would use an automatic translation tool if needed. I didn’t mean to rely on it exclusively or create any ambiguity.
      However, here’s an explicit translation of my comment: I always tell this to my students, but I don’t understand why it’s mentioned so rarely! Probably as rarely as pointers to class members, like `int Car::* pSpeed = &Car::speed;`.
      Thanks for understanding!

  • @PaulMetalhero
    @PaulMetalhero 3 місяці тому +2

    This is great. But for the && overload, I think it should be better to call it something like 'take_ownership', to make it more expressive

  • @MarcelRobitaille
    @MarcelRobitaille 3 місяці тому

    I didn't know you could std::move a const. Doesn't that break const correctness? It leaves the variable in a moved from state.

    • @evo6789
      @evo6789 3 місяці тому +3

      No, std::move is kind of a misnomer. It doesn't actually move anything, it just does a static cast to T&&. So if you std::move a const object it will just return the object as const T&& but the internal state will not have changed at all by std::move

    • @avramlevitter6150
      @avramlevitter6150 3 місяці тому

      @@evo6789 And, pretty importantly, if you have a copy constructor as well as a move constructor (that takes a non-const rvalue) the copy constructor will be valid (rvalue -> lvalue) while the move constructor won't (because the const can't be removed). So you end up with a copy.

    • @MarcelRobitaille
      @MarcelRobitaille 3 місяці тому +2

      @@evo6789 You're right. So it would be impossible to do `std::move(obj).set_value(...)`, but `std::move(obj).get_value()` is fine because it calls the `get_value() const &&` overload.

  • @obinator9065
    @obinator9065 3 місяці тому +2

    i hate water

  • @avramlevitter6150
    @avramlevitter6150 3 місяці тому +1

    1:55 I was just saying to myself that this would be a great time to mention deducing this, and then you anticipated me a little *too* well