简体   繁体   中英

Missing something like std::forward for this

I believe I understand the concept of perfect forwarding in C++. But I'm missing something similar for "this". Let's have:

class X {
  std::vector<int> vec;
public:
  std::vector<int> const &getVec() const & { return vec; }
  std::vector<int> &      getVec() &       { return vec; }
  std::vector<int>        getVec() &&      { return std::move(vec); }
};

As std::forward() compresses similar definitions for parameters, I'd like to have something similar for "this". Something like:

class X {
  std::vector<int> vec;
public:
  auto &&getVec() /*???const &*/ { return thisForward(vec); }
};

Is something like the above possible (or available in STL or boost)? Or, at least, is there some workaround?

Plus an additional question (just for verification), does my 3 overloads of getVec() above make a sense? Is it okay (for optimization reasons) to have such 3 overloads? (Assume "X" is a container-like class.)

For C++20, you can get close but I don't see how doing exactly what you ask is possible.

If you are willing to turn get into a static/friend function it works. I couldn't get the repetition of a helper type down to zero, but maybe this can be improved on. If you are willing to pay a reference member, this can likely be achieved with a wrapper but that wrapper would have to store a reference to the member.

#pragma once
#include <type_traits>

template <typename T>
concept lvalue = std::is_lvalue_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>;
template <typename T>
concept lcvalue = std::is_lvalue_reference_v<T> && std::is_const_v<std::remove_reference_t<T>>;

template <typename T, typename R>
struct RT {
  using type = R;
};
template <lvalue T, typename R>
struct RT<T,R> {
  using type = std::add_lvalue_reference_t<std::remove_const_t<R>>;
};
template <lcvalue T, typename R>
struct RT<T,R> {
  using type = std::add_lvalue_reference_t<std::add_const_t<R>>;
};

template <typename T, typename R>
using RT_t = typename RT<T,R>::type;

Example:

#include <vector>
#include <cassert>
#include "rt.h"

namespace {
  struct X {
    std::vector<int> v;

    template <typename Self>
    friend RT_t<Self,std::vector<int>> get(Self&& self) {
      return std::forward<RT_t<Self,std::vector<int>>>(self.v);
    }
  };
}

int main() {
  X x{std::vector{1,2,3}};
  assert(x.v.size() == 3);
  X& xr = x;
  get(xr).push_back(1);
  assert(x.v.size() == 4);
  //X const& xc = x;
  //get(xc).push_back(1); // does not compile
  {
    std::vector<int> vec = get(std::move(x));
    assert(vec.size() == 4);
  }
  assert(x.v.size() == 0);
}

So, downsides:

  • One has to write get(x) instead of x.get()
  • When defining get one has to repeat RT_t<Self,std::vector<int>>

Demo on compiler explorer .

As of C++23 it will be possible to declare the this parameter explicitly . Then you will be able to do what you are asking for:

class X {
    std::vector<int> vec;
public:
    template<class Self>
    decltype(auto) getVec(this Self&& self) {
        return std::forward<Self>(self).vec;
    }
};

You can read more here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM