home | help
std::forward_like(3)	       C++ Standard Libary	    std::forward_like(3)

NAME
     std::forward_like - std::forward_like

Synopsis
	Defined in header <utility>
	template<  class  T, class U >					  (since
     C++23)
	[[nodiscard]] constexpr auto&& forward_like( U&& x ) noexcept;

	Returns a reference to x which has similar properties to T&&.

	The return type is determined as below:

	 1. If std::remove_reference_t<T> is a const-qualified	type,  then  the
     referenced
	    type  of the return type is const std::remove_reference_t<U>. Other-
     wise, the
	    referenced type is std::remove_reference_t<U>.
	 2. If T&& is an lvalue reference type, then the return type is also  an
     lvalue
	    reference  type.  Otherwise,  the return type is an rvalue reference
     type.

	If T is not a referenceable type, the program is ill-formed.

Parameters
	x - a value needs to be forwarded like type T

Return value
	A reference to x of the type determined as above.

Notes
	Like std::forward, std::move, and std::as_const, std::forward_like is  a
     type cast
	that only influences the value category of an expression, or potentially
     adds
	const-qualification.

	When m is an actual member and thus o.m a valid expression, this is usu-
     ally spelled
	as std::forward<decltype(o)>(o).m in C++20 code.

	This leads to three possible models, called merge, tuple, and language.

	  *  merge:  merge the const qualifiers, and adopt the value category of
     the Owner.
	  * tuple: what std::get<0>(Owner) does, assuming Owner  is  a	std::tu-
     ple<Member>.
	  * language: what std::forward<decltype(Owner)>(o).m does.

	The main scenario that std::forward_like caters to is adapting afara ob-
     jects.
	Neither the tuple nor the language scenarios do the right thing for that
     main
	use-case, so the merge model is used for std::forward_like.

	  Feature-test macro	Value	 Std	    Feature
	__cpp_lib_forward_like 202207L (C++23) std::forward_like

Possible implementation
	template<class T, class U>
	[[nodiscard]] constexpr auto&& forward_like(U&& x) noexcept
	{
	    constexpr  bool is_adding_const = std::is_const_v<std::remove_refer-
     ence_t<T>>;
	    if constexpr (std::is_lvalue_reference_v<T&&>)
	    {
		if constexpr (is_adding_const)
		    return std::as_const(x);
		else
		    return static_cast<U&>(x);
	    }
	    else
	    {
		if constexpr (is_adding_const)
		    return std::move(std::as_const(x));
		else
		    return std::move(x);
	    }
	}

Example
     // Run this code

      #include <cstddef>
      #include <iostream>
      #include <memory>
      #include <optional>
      #include <type_traits>
      #include <utility>
      #include <vector>

      struct TypeTeller
      {
	  void operator()(this auto&& self)
	  {
	      using SelfType = decltype(self);
	      using UnrefSelfType = std::remove_reference_t<SelfType>;
	      if constexpr (std::is_lvalue_reference_v<SelfType>)
	      {
		  if constexpr (std::is_const_v<UnrefSelfType>)
		      std::cout << "const lvalue\n";
		  else
		      std::cout << "mutable lvalue\n";
	      }
	      else
	      {
		  if constexpr (std::is_const_v<UnrefSelfType>)
		      std::cout << "const rvalue\n";
		  else
		      std::cout << "mutable rvalue\n";
	      }
	  }
      };

      struct FarStates
      {
	  std::unique_ptr<TypeTeller> ptr;
	  std::optional<TypeTeller> opt;
	  std::vector<TypeTeller> container;

	  auto&& from_opt(this auto&& self)
	  {
	      return std::forward_like<decltype(self)>(self.opt.value());
	      // It is OK to use std::forward<decltype(self)>(self).opt.value(),
	      // because std::optional provides suitable accessors.
	  }

	  auto&& operator[](this auto&& self, std::size_t i)
	  {
	      return std::forward_like<decltype(self)>(self.container.at(i));
	      // It is not so good to use std::forward<decltype(self)>(self)[i],
     because
	      // containers do not provide  rvalue  subscript  access,	although
     they could.
	  }

	  auto&& from_ptr(this auto&& self)
	  {
	      if (!self.ptr)
		  throw std::bad_optional_access{};
	      return std::forward_like<decltype(self)>(*self.ptr);
	      //  It is not good to use *std::forward<decltype(self)>(self).ptr,
     because
	      // std::unique_ptr<TypeTeller> always dereferences to a  non-const
     lvalue.
	  }
      };

      int main()
      {
	  FarStates my_state
	  {
	      .ptr{std::make_unique<TypeTeller>()},
	      .opt{std::in_place, TypeTeller{}},
	      .container{std::vector<TypeTeller>(1)},
	  };

	  my_state.from_ptr()();
	  my_state.from_opt()();
	  my_state[0]();

	  std::cout << '\n';

	  std::as_const(my_state).from_ptr()();
	  std::as_const(my_state).from_opt()();
	  std::as_const(my_state)[0]();

	  std::cout << '\n';

	  std::move(my_state).from_ptr()();
	  std::move(my_state).from_opt()();
	  std::move(my_state)[0]();

	  std::cout << '\n';

	  std::move(std::as_const(my_state)).from_ptr()();
	  std::move(std::as_const(my_state)).from_opt()();
	  std::move(std::as_const(my_state))[0]();

	  std::cout << '\n';
      }

Output:
      mutable lvalue
      mutable lvalue
      mutable lvalue

      const lvalue
      const lvalue
      const lvalue

      mutable rvalue
      mutable rvalue
      mutable rvalue

      const rvalue
      const rvalue
      const rvalue

See also
	move	 obtains an rvalue reference
	(C++11)  (function template)
	forward  forwards a function argument
	(C++11)  (function template)
	as_const obtains a reference to const to its argument
	(C++17)  (function template)

http://cppreference.com 	   2024.06.10		    std::forward_like(3)

home | help