Skip to content

fix universal: fix ForwardLike in C++23 to behave same as std::forwad_like#1246

Open
emikg777 wants to merge 1 commit into
userver-framework:developfrom
emikg777:fix/universal-forward_like-cxx23
Open

fix universal: fix ForwardLike in C++23 to behave same as std::forwad_like#1246
emikg777 wants to merge 1 commit into
userver-framework:developfrom
emikg777:fix/universal-forward_like-cxx23

Conversation

@emikg777

@emikg777 emikg777 commented Jun 7, 2026

Copy link
Copy Markdown

utils::ForwardLike used return x; for the non-const lvalue-like case.

This no longer matches std::forward_like in C++23. Since P2266R3 ("Simpler implicit move"), the C++23 return statement rules treat a move-eligible returned expression as an xvalue. In standard wording this is the return statement wording in [stmt.return]: if the returned expression is move-eligible, it is treated as an xvalue. std::forward_like<T> still has to preserve the reference category requested by T&&, so ForwardLike<int&>(...) must produce an lvalue reference.

The existing forward_like_test.cpp fails with gcc 15.2.0:

/home/egoncharov/bcore/cmake-build-debug/_deps/userver-src/universal/src/utils/forward_like_test.cpp:55:27:   required from here
   55 |     TestsTwoForwards<int&>(char{});
      |     ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
/home/egoncharov/bcore/cmake-build-debug/_deps/userver-src/universal/src/utils/forward_like_test.cpp:36:24: error: static assertion failed
   36 |     static_assert(std::is_same_v<
      |                   ~~~~~^~~~~~~~~~
   37 |                   decltype(std::forward_like<T>(std::forward<U>(x))),
      |                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   38 |                   decltype(utils::ForwardLike<T>(std::forward<U>(x)))>);
      |                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/egoncharov/bcore/cmake-build-debug/_deps/userver-src/universal/src/utils/forward_like_test.cpp:36:24: note: 'std::is_same_v<char&, char&&>' evaluates to false

This change implements ForwardLike via explicit casts to the expected reference type instead of relying on return x, std::move, or std::as_const.

Here is minimal demonsration of difference between original ForwardLike and std::forward_like in C++23:
https://godbolt.org/z/s7WdxYaWe

The implementation now:

  • propagates const from T to the returned reference type;
  • uses T&& to decide whether the result should be an lvalue reference or an rvalue reference;
  • avoids dependence on C++23 return statement implicit move rules;
  • matches std::forward_like semantics.

Testing:

  • Built a userver-based project with CMAKE_CXX_STANDARD=23.
  • Built and ran userver-universal-unittest.
  • Full userver-universal-unittest run: 1671/1672 tests passed. The only failure was LogFilepath.UserverCroppedCorrectly, which appears to be path-layout-sensitive when userver is built as a CPM dependency under _deps/userver-src.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant