[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