[libcxx-commits] [libcxx] [libcxx] Implement `std::constant_wrapper` (PR #191695)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Apr 17 11:06:15 PDT 2026
================
@@ -0,0 +1,590 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// ADDITIONAL_COMPILE_FLAGS: -Wno-constant-logical-operand
+
+// constant_wrapper
+
+// template<constexpr-param L, constexpr-param R>
+// friend constexpr auto operator+(L, R) noexcept -> constant_wrapper<(L::value + R::value)>
+// { return {}; }
+// template<constexpr-param L, constexpr-param R>
+// friend constexpr auto operator-(L, R) noexcept -> constant_wrapper<(L::value - R::value)>
+// { return {}; }
+// template<constexpr-param L, constexpr-param R>
+// friend constexpr auto operator*(L, R) noexcept -> constant_wrapper<(L::value * R::value)>
+// { return {}; }
+// template<constexpr-param L, constexpr-param R>
+// friend constexpr auto operator/(L, R) noexcept -> constant_wrapper<(L::value / R::value)>
+// { return {}; }
+// template<constexpr-param L, constexpr-param R>
+// friend constexpr auto operator%(L, R) noexcept -> constant_wrapper<(L::value % R::value)>
+// { return {}; }
+
+// template<constexpr-param L, constexpr-param R>
+// friend constexpr auto operator<<(L, R) noexcept -> constant_wrapper<(L::value << R::value)>
+// { return {}; }
+// template<constexpr-param L, constexpr-param R>
+// friend constexpr auto operator>>(L, R) noexcept -> constant_wrapper<(L::value >> R::value)>
+// { return {}; }
+// template<constexpr-param L, constexpr-param R>
+// friend constexpr auto operator&(L, R) noexcept -> constant_wrapper<(L::value & R::value)>
+// { return {}; }
+// template<constexpr-param L, constexpr-param R>
+// friend constexpr auto operator|(L, R) noexcept -> constant_wrapper<(L::value | R::value)>
+// { return {}; }
+// template<constexpr-param L, constexpr-param R>
+// friend constexpr auto operator^(L, R) noexcept -> constant_wrapper<(L::value ^ R::value)>
+// { return {}; }
+
+// template<constexpr-param L, constexpr-param R>
+// requires (!is_constructible_v<bool, decltype(L::value)> ||
+// !is_constructible_v<bool, decltype(R::value)>)
+// friend constexpr auto operator&&(L, R) noexcept
+// -> constant_wrapper<(L::value && R::value)>
+// { return {}; }
+// template<constexpr-param L, constexpr-param R>
+// requires (!is_constructible_v<bool, decltype(L::value)> ||
+// !is_constructible_v<bool, decltype(R::value)>)
+// friend constexpr auto operator||(L, R) noexcept
+// -> constant_wrapper<(L::value || R::value)>
+// { return {}; }
+
+#include <cassert>
+#include <concepts>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "helpers.h"
+#include "test_macros.h"
+
+struct WithOps {
+ int value;
+
+ constexpr WithOps(int v) : value(v) {}
+
+ friend constexpr auto operator+(WithOps l, WithOps r) { return WithOps{l.value + r.value}; }
+ friend constexpr auto operator-(WithOps l, WithOps r) { return WithOps{l.value - r.value}; }
+ friend constexpr auto operator*(WithOps l, WithOps r) { return WithOps{l.value * r.value}; }
+ friend constexpr auto operator/(WithOps l, WithOps r) { return WithOps{l.value / r.value}; }
+ friend constexpr auto operator%(WithOps l, WithOps r) { return WithOps{l.value % r.value}; }
+ friend constexpr auto operator<<(WithOps l, WithOps r) { return WithOps{l.value << r.value}; }
+ friend constexpr auto operator>>(WithOps l, WithOps r) { return WithOps{l.value >> r.value}; }
+ friend constexpr auto operator&(WithOps l, WithOps r) { return WithOps{l.value & r.value}; }
+ friend constexpr auto operator|(WithOps l, WithOps r) { return WithOps{l.value | r.value}; }
+ friend constexpr auto operator^(WithOps l, WithOps r) { return WithOps{l.value ^ r.value}; }
+
+ friend constexpr auto operator&&(WithOps l, WithOps r) { return WithOps{l.value && r.value}; }
+ friend constexpr auto operator||(WithOps l, WithOps r) { return WithOps{l.value || r.value}; }
+};
+
+struct OptsReturnNonStructural {
+ int value;
+
+ constexpr OptsReturnNonStructural(int v) : value(v) {}
+
+ friend constexpr auto operator+(OptsReturnNonStructural l, OptsReturnNonStructural r) {
+ return NonStructural{l.value + r.value};
+ }
+ friend constexpr auto operator-(OptsReturnNonStructural l, OptsReturnNonStructural r) {
+ return NonStructural{l.value - r.value};
+ }
+ friend constexpr auto operator*(OptsReturnNonStructural l, OptsReturnNonStructural r) {
+ return NonStructural{l.value * r.value};
+ }
+ friend constexpr auto operator/(OptsReturnNonStructural l, OptsReturnNonStructural r) {
+ return NonStructural{l.value / r.value};
+ }
+ friend constexpr auto operator%(OptsReturnNonStructural l, OptsReturnNonStructural r) {
+ return NonStructural{l.value % r.value};
+ }
+ friend constexpr auto operator<<(OptsReturnNonStructural l, OptsReturnNonStructural r) {
+ return NonStructural{l.value << r.value};
+ }
+ friend constexpr auto operator>>(OptsReturnNonStructural l, OptsReturnNonStructural r) {
+ return NonStructural{l.value >> r.value};
+ }
+ friend constexpr auto operator&(OptsReturnNonStructural l, OptsReturnNonStructural r) {
+ return NonStructural{l.value & r.value};
+ }
+ friend constexpr auto operator|(OptsReturnNonStructural l, OptsReturnNonStructural r) {
+ return NonStructural{l.value | r.value};
+ }
+ friend constexpr auto operator^(OptsReturnNonStructural l, OptsReturnNonStructural r) {
+ return NonStructural{l.value ^ r.value};
+ }
+ friend constexpr auto operator&&(OptsReturnNonStructural l, OptsReturnNonStructural r) {
+ return NonStructural{l.value && r.value};
+ }
+ friend constexpr auto operator||(OptsReturnNonStructural l, OptsReturnNonStructural r) {
+ return NonStructural{l.value || r.value};
+ }
+};
+
+struct NoOps {};
+
+template <class L, class R>
+concept HasPlus = requires(L l, R r) {
+ { l + r };
+};
+
+template <class L, class R>
+concept HasMinus = requires(L l, R r) {
+ { l - r };
+};
+
+template <class L, class R>
+concept HasMultiply = requires(L l, R r) {
+ { l * r };
+};
+
+template <class L, class R>
+concept HasDivide = requires(L l, R r) {
+ { l / r };
+};
+
+template <class L, class R>
+concept HasModulo = requires(L l, R r) {
+ { l % r };
+};
+
+template <class L, class R>
+concept HasShiftLeft = requires(L l, R r) {
+ { l << r };
+};
+
+template <class L, class R>
+concept HasShiftRight = requires(L l, R r) {
+ { l >> r };
+};
+
+template <class L, class R>
+concept HasBitAnd = requires(L l, R r) {
+ { l & r };
+};
+
+template <class L, class R>
+concept HasBitOr = requires(L l, R r) {
+ { l | r };
+};
+
+template <class L, class R>
+concept HasBitXor = requires(L l, R r) {
+ { l ^ r };
+};
+
+template <class L, class R>
+concept HasLogicalAnd = requires(L l, R r) {
+ { l && r };
+};
+
+template <class L, class R>
+concept HasLogicalOr = requires(L l, R r) {
+ { l || r };
+};
+
+template <class L, class R>
+concept HasNoexceptPlus = requires(L l, R r) {
+ { l + r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptMinus = requires(L l, R r) {
+ { l - r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptMultiply = requires(L l, R r) {
+ { l * r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptDivide = requires(L l, R r) {
+ { l / r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptModulo = requires(L l, R r) {
+ { l % r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptShiftLeft = requires(L l, R r) {
+ { l << r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptShiftRight = requires(L l, R r) {
+ { l >> r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptBitAnd = requires(L l, R r) {
+ { l & r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptBitOr = requires(L l, R r) {
+ { l | r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptBitXor = requires(L l, R r) {
+ { l ^ r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptLogicalAnd = requires(L l, R r) {
+ { l && r } noexcept;
+};
+
+template <class L, class R>
+concept HasNoexceptLogicalOr = requires(L l, R r) {
+ { l || r } noexcept;
+};
+
+// Concept checks for int + int operations
+static_assert(HasPlus<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasMinus<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasMultiply<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasDivide<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasModulo<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasShiftLeft<std::constant_wrapper<6>, std::constant_wrapper<1>>);
+static_assert(HasShiftRight<std::constant_wrapper<6>, std::constant_wrapper<1>>);
+static_assert(HasBitAnd<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasBitOr<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasBitXor<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasLogicalAnd<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasLogicalOr<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+
+static_assert(HasNoexceptPlus<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptMinus<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptMultiply<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptDivide<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptModulo<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptShiftLeft<std::constant_wrapper<6>, std::constant_wrapper<1>>);
+static_assert(HasNoexceptShiftRight<std::constant_wrapper<6>, std::constant_wrapper<1>>);
+static_assert(HasNoexceptBitAnd<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptBitOr<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptBitXor<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptLogicalAnd<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+static_assert(HasNoexceptLogicalOr<std::constant_wrapper<6>, std::constant_wrapper<3>>);
+
+// NoOps
+static_assert(!HasPlus<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasMinus<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasMultiply<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasDivide<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasModulo<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasShiftLeft<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasShiftRight<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasBitAnd<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasBitOr<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasBitXor<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasLogicalAnd<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+static_assert(!HasLogicalOr<std::constant_wrapper<NoOps{}>, std::constant_wrapper<NoOps{}>>);
+
+// Concept checks for WithOps operations
+static_assert(HasNoexceptPlus<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptMinus<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptMultiply<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptDivide<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptModulo<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptShiftLeft<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{1}>>);
+static_assert(HasNoexceptShiftRight<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{1}>>);
+static_assert(HasNoexceptBitAnd<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptBitOr<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptBitXor<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptLogicalAnd<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+static_assert(HasNoexceptLogicalOr<std::constant_wrapper<WithOps{6}>, std::constant_wrapper<WithOps{3}>>);
+
+// Non-structural types use implicit conversion to underlying type
+static_assert(
----------------
ldionne wrote:
I would like to suggest that we use `// clang-format off` on these long lines. I think the resulting code is going to be easier to read if everything it somewhat aligned.
https://github.com/llvm/llvm-project/pull/191695
More information about the libcxx-commits
mailing list