Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to detect if the current member function is operating on an lvalue or rvalue?

Tags:

c++

c++17

Qualifiers are a great way to overload functions based on context of the object. Such qualifiers are const and volatile. However, writing code for them is annoying as they are usually duplication of code as the cv qualifiers are at least propagated by this. As such, we can make the entire function (sans cv qualifiers) exactly the same using propagation tricks and with the help of auto or decltype(auto).

E.g.:

struct X {
  int m_i = 0;

  auto& i()                &  { return m_i ; } // returns int                &
  auto& i()       volatile &  { return m_i ; } // returns int       volatile &
  auto& i() const          &  { return m_i ; } // returns int const          &
  auto& i() const volatile &  { return m_i ; } // returns int const volatile &
};

Although annoying, at least we can see that this is going to work in all contexts with the same code and thus minimizes error.

However, with the new lvalue/rvalue qualifiers that can be used to specify member functions, this doesn't propagate whether the function was called on an object that is an lvalue or rvalue. This results in having to make slight variations of the function, if say I wanted to do this:

struct X {
  int m_i = 0;

  auto&& i()                &  { return           m_i ; } // returns int                &
  auto&& i()       volatile &  { return           m_i ; } // returns int       volatile &
  auto&& i() const          &  { return           m_i ; } // returns int const          &
  auto&& i() const volatile &  { return           m_i ; } // returns int const volatile &

  auto&& i()                && { return std::move(m_i); } // returns int                &&
  auto&& i()       volatile && { return std::move(m_i); } // returns int       volatile &&
  auto&& i() const          && { return std::move(m_i); } // returns int const          &&
  auto&& i() const volatile && { return std::move(m_i); } // returns int const volatile &&
};

It would be nice to be able to make them all the same.

Something like this:

struct X {
  int m_i = 0;

  decltype(auto) i()                &  { return std::forward<...some_magic...>(m_i); } // returns int                &
  decltype(auto) i()       volatile &  { return std::forward<...some_magic...>(m_i); } // returns int       volatile &
  decltype(auto) i() const          &  { return std::forward<...some_magic...>(m_i); } // returns int const          &
  decltype(auto) i() const volatile &  { return std::forward<...some_magic...>(m_i); } // returns int const volatile &

  decltype(auto) i()                && { return std::forward<...some_magic...>(m_i); } // returns int                &&
  decltype(auto) i()       volatile && { return std::forward<...some_magic...>(m_i); } // returns int       volatile &&
  decltype(auto) i() const          && { return std::forward<...some_magic...>(m_i); } // returns int const          &&
  decltype(auto) i() const volatile && { return std::forward<...some_magic...>(m_i); } // returns int const volatile &&
};

Is there any means to interrogate if the current member function is operating on an lvalue or rvalue?

like image 884
Adrian Avatar asked Apr 28 '19 16:04

Adrian


1 Answers

No, there isn't.

The only access you have to yourself within the confines of a member function body is: this. And this is always a X [cv]* const, and *this is always an lvalue - an X [cv]&. It can tell you what the cv-qualifiers of the member function are, but it cannot tell you what the ref-qualifiers of the function are.

You would have to indirect all of your member functions to a non-member function, passing yourself approrpiately as an lvalue or rvalue:

decltype(auto) i() &  { return free_i(*this); }
decltype(auto) i() && { return free_i(std::move(*this)); }

template <typename Self>
friend decltype(auto) free_i(Self&& self) { ... }

P0847 solves this as well, but that won't be in C++20 either. Maybe C++23:

template <typename Self>
decltype(auto) i(this Self&& self) { ... }

Here, you can straightforwardly pull off what kind of reference self is.

like image 78
Barry Avatar answered Nov 11 '22 12:11

Barry