[libcxx-commits] [libcxx] [libc++] Implement `std::function_ref` (PR #186692)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Jun 19 11:18:44 PDT 2026
================
@@ -0,0 +1,138 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// In case the function signature is taking an argument by value,
+// when the type is small and trivial, we pass it internally by value,
+// otherwise, we pass it by rvalue reference
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+struct Small {};
+
+struct Big {
+ char c[128];
+};
+
+struct SmallButNonTrivial {
+ Small s;
+ SmallButNonTrivial() = default;
+ SmallButNonTrivial(const SmallButNonTrivial&) {}
+};
+
+// Need to inspect the internal state of function_ref, and our implementation friend-ed all the function_ref
+// specializations, so we can just specialize an undefined function_ref here to inspect the defined ones
+template <>
+class std::function_ref<int> {
+ using storage = std::__function_ref_storage;
+ void test() {
+ {
+ // by value small argument
+ // the internal function should pass by value
+ using call = std::function_ref<void(Small)>::__call_t;
+ static_assert(std::is_same_v<call, void (*)(storage, Small)>);
+ }
+ {
+ // by value big argument
+ // the internal function should pass by rvalue reference
+ using call = std::function_ref<void(Big)>::__call_t;
+ static_assert(std::is_same_v<call, void (*)(storage, Big&&)>);
+ }
+ {
+ // by value non-trivial small argument
+ // the internal function should pass by rvalue reference to avoid unnecessary copy/move
+ using call = std::function_ref<void(SmallButNonTrivial)>::__call_t;
+ static_assert(std::is_same_v<call, void (*)(storage, SmallButNonTrivial&&)>);
+ }
+ {
+ // by lvalue reference argument
+ // the internal function should pass by lvalue reference
+ using call = std::function_ref<void(Small&)>::__call_t;
+ static_assert(std::is_same_v<call, void (*)(storage, Small&)>);
+ }
+ {
+ // by rvalue reference argument
+ // the internal function should pass by rvalue reference
+ using call = std::function_ref<void(Small&&)>::__call_t;
+ static_assert(std::is_same_v<call, void (*)(storage, Small&&)>);
+ }
+ }
+};
+
+struct TrackCopyMove {
+ mutable int copy_count = 0;
+ int move_count = 0;
+
+ TrackCopyMove() = default;
+ TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
+ ++copy_count;
+ ++other.copy_count;
+ }
+
+ TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
+ ++move_count;
+ ++other.move_count;
+ }
+ TrackCopyMove& operator=(const TrackCopyMove& other) {
+ ++copy_count;
+ ++other.copy_count;
+ return *this;
+ }
+ TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
+ ++move_count;
+ ++other.move_count;
+ return *this;
+ }
+};
+
+void test() {
----------------
ldionne wrote:
I think there are two tests in this file. First, there's the quality-of-implementation codegen test above for trivial types.
Second, there's this test, which is actually (mostly) not specific to libc++. I'd move these observable tests to `libcxx/test/std`, with perhaps the third test-case for prvalues under `#if _LIBCPP_VERSION` if that one isn't standard-mandated.
For the first test about codegen, I'd use `FileCheck`, which we now support optionally in the libc++ test suite. That should allow you to do a normal codegen test like we do for LLVM.
https://github.com/llvm/llvm-project/pull/186692
More information about the libcxx-commits
mailing list