Object rvalue propagation for member function calls
Asked Answered
P

2

6

I have a struct F with a function foo that has different implementation whether F is a temporary or not

struct  F{
  void foo() &  { std::cout << "F::foo() &" << std::endl; }
  void foo() && { std::cout << "F::foo() &&" << std::endl; }
};

Another struct A has a copy of F and in its function bar calls F::foo. I want to use the correct version of F::foo(). Therefore the implementation is:

struct A{

  void bar() & {
    f.foo();
  }

  void bar() && {
    std::move(f).foo();
  }

  F f;
};

I'm wondering if I really have to provide two implementations of A::bar(). Isn't there a smart way to use std::forward to automatically decide which F::foo() should be used?


An attempt:

struct B{

  void bar() {
    std::forward<F>(f).foo();
  }
  
  F f;
};

However, this does not work. It calls F::foo() && every time.


Complete example

Preamble answered 14/10, 2020 at 11:28 Comment(0)
S
5

You can use a non-member function template:

struct B{

  template<typename TB>
  friend void bar(TB&& self) {
    std::forward<TB>(self).f.foo();
  }
  
  F f;
};

Depending on other function parameters, you might want to restrict the type of TB such that is_base_of_v<B, remove_const_t<remove_reference_t<TB>>>.

Sherbet answered 14/10, 2020 at 11:48 Comment(2)
Awesome hack. I was looking for a friend based solution, too, but ended up with something less elegent. Nice!Lundberg
@Lundberg You just know too much C++17. For me, if constexpr is somewhere at the back of my mind and forward is much more present ;)Sherbet
L
5

No, there is no shortcut available at this point and you will need to stick to the verbose version. But have a look at p0847, "Deducing this". Not sure what the status of this proposal is, however. From the abstract:

We propose a new mechanism for specifying or deducing the value category of an instance of a class. In other words, a way to tell from within a member function whether the object it’s invoked on is an lvalue or an rvalue, and whether it is const or volatile.

Lundberg answered 14/10, 2020 at 11:33 Comment(0)
S
5

You can use a non-member function template:

struct B{

  template<typename TB>
  friend void bar(TB&& self) {
    std::forward<TB>(self).f.foo();
  }
  
  F f;
};

Depending on other function parameters, you might want to restrict the type of TB such that is_base_of_v<B, remove_const_t<remove_reference_t<TB>>>.

Sherbet answered 14/10, 2020 at 11:48 Comment(2)
Awesome hack. I was looking for a friend based solution, too, but ended up with something less elegent. Nice!Lundberg
@Lundberg You just know too much C++17. For me, if constexpr is somewhere at the back of my mind and forward is much more present ;)Sherbet

© 2022 - 2024 — McMap. All rights reserved.