[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:47 PDT 2026


================
@@ -0,0 +1,359 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template<auto f, class T>
+//   constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
+
+#include <cassert>
+#include <functional>
+#include <utility>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// Constraints: is-invocable-using<const F&, cv T*> is true.
+
+auto l1                = [](int*) {};
+auto l1_const          = [](const int*) {};
+auto l1_noexcept       = [](int*) noexcept {};
+auto l1_const_noexcept = [](const int*) noexcept {};
+auto l2                = [](int*, double) {};
+auto l2_const          = [](const int*, double) {};
+auto l2_noexcept       = [](int*, double) noexcept {};
+auto l2_const_noexcept = [](const int*, double) noexcept {};
+
+struct NonConstInvocable {
+  void operator()(int*) noexcept {}
+};
+
+struct A {
+  int i;
+  void f() {}
+  void f_const() const {}
+  void f_noexcept() noexcept {}
+  void f_const_noexcept() const noexcept {}
+  void g(int&) {}
+  void g_const(int&) const {}
+  void g_noexcept(int&) noexcept {}
+  void g_const_noexcept(int&) const noexcept {}
+};
+
+// non-const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, const int*>);
+
+// LWG issue 4256
+// https://cplusplus.github.io/LWG/issue4256
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
+
+// non-const noexcept
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A*>);
+
+// the constructor is noexcept
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+static_assert(
+    std::
+        is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+
+// const noexcept(false)
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
+
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2_const>, int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
+static_assert(
+    std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
+
+// const noexcept
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const_noexcept>, int*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const>, int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
+
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<NonConstInvocable{}>,
+                                       int*>);
+
+static_assert(std::is_constructible_v<std::function_ref<void(double) const noexcept>,
+                                      std::constant_wrapper<l2_const_noexcept>,
+                                      int*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_const_noexcept>, int*>);
+
+static_assert(
+    std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A*>);
+static_assert(
+    !std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
+static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
+                                       std::constant_wrapper<&A::g_const_noexcept>,
+                                       A*>);
+
+// the constructor is noexcept
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<l1_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const noexcept>,
+                                              std::constant_wrapper<l2_const_noexcept>,
+                                              int*>);
+static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
+                                              std::constant_wrapper<&A::f_const_noexcept>,
+                                              A*>);
+
+double f1(const int* x, double y) noexcept { return *x + y; }
+
+struct M {
+  int i;
+  int f() { return i; }
+  int f_const() const { return i + 5; }
+  int f_noexcept() noexcept { return i + 7; }
+  int f_const_noexcept() const noexcept { return i + 9; }
+  int g(int& j) {
+    j = 42;
+    return i + j;
+  }
+  int g_const(int& j) const {
+    j = 42;
+    return i + j + 1;
+  }
+  int g_noexcept(int& j) noexcept {
+    j = 42;
+    return i + j + 2;
+  }
+  int g_const_noexcept(int& j) const noexcept {
+    j = 42;
+    return i + j + 3;
+  }
+};
+
+struct Int {
+  int i;
+  constexpr Int(int ii) noexcept : i(ii) {}
+};
+
+struct NeedsConversion {
+  int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
+};
+
+int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
+
+constexpr bool test() {
+  {
+    int i = 0;
+    std::function_ref<void()> f(std::cw<[](int*) {}>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // explicit
+    int i                       = 0;
+    std::function_ref<void()> f = {std::cw<[](int*) {}>, &i};
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      f();
+    }
+  }
+  {
+    // mutate
+    int i;
+    std::function_ref<double(double)> f(
+        std::cw<[](int* j, double d) {
+          *j = 5;
+          return *j + d;
+        }>,
+        &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(3.3) == 8.3);
+      assert(i == 5);
+    }
+  }
+  {
+    // const
+    int i = 5;
+    std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f() == 47);
+    }
+  }
+  {
+    // noexcept
+    int i = 5;
+    std::function_ref<double(double) noexcept> f(std::cw<&f1>, &i);
+
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // const noexcept
+    int i = 5;
+    std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
+    if (!TEST_IS_CONSTANT_EVALUATED) {
+      assert(f(2.0) == 7.0);
+    }
+  }
+  {
+    // member ptr
----------------
ldionne wrote:

The sub test cases in this scope are pretty difficult to follow. I think making them more independent with their own scope (even though that will mean a bit more duplication) would make this easier to follow. Also applies to `constant_wrapper_ref.pass.cpp`.

https://github.com/llvm/llvm-project/pull/186692


More information about the libcxx-commits mailing list