//===----------------------------------------------------------------------===// // // 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 // friend constexpr auto operator+(L, R) noexcept -> constant_wrapper<(L::value + R::value)> // { return {}; } // template // friend constexpr auto operator-(L, R) noexcept -> constant_wrapper<(L::value - R::value)> // { return {}; } // template // friend constexpr auto operator*(L, R) noexcept -> constant_wrapper<(L::value * R::value)> // { return {}; } // template // friend constexpr auto operator/(L, R) noexcept -> constant_wrapper<(L::value / R::value)> // { return {}; } // template // friend constexpr auto operator%(L, R) noexcept -> constant_wrapper<(L::value % R::value)> // { return {}; } // template // friend constexpr auto operator<<(L, R) noexcept -> constant_wrapper<(L::value << R::value)> // { return {}; } // template // friend constexpr auto operator>>(L, R) noexcept -> constant_wrapper<(L::value >> R::value)> // { return {}; } // template // friend constexpr auto operator&(L, R) noexcept -> constant_wrapper<(L::value & R::value)> // { return {}; } // template // friend constexpr auto operator|(L, R) noexcept -> constant_wrapper<(L::value | R::value)> // { return {}; } // template // friend constexpr auto operator^(L, R) noexcept -> constant_wrapper<(L::value ^ R::value)> // { return {}; } // template // requires (!is_constructible_v || // !is_constructible_v) // friend constexpr auto operator&&(L, R) noexcept // -> constant_wrapper<(L::value && R::value)> // { return {}; } // template // requires (!is_constructible_v || // !is_constructible_v) // friend constexpr auto operator||(L, R) noexcept // -> constant_wrapper<(L::value || R::value)> // { return {}; } #include #include #include #include #include "helpers.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 OpsReturnNonStructural { int value; constexpr OpsReturnNonStructural(int v) : value(v) {} friend constexpr auto operator+(OpsReturnNonStructural l, OpsReturnNonStructural r) { return NonStructural{l.value + r.value}; } friend constexpr auto operator-(OpsReturnNonStructural l, OpsReturnNonStructural r) { return NonStructural{l.value - r.value}; } friend constexpr auto operator*(OpsReturnNonStructural l, OpsReturnNonStructural r) { return NonStructural{l.value * r.value}; } friend constexpr auto operator/(OpsReturnNonStructural l, OpsReturnNonStructural r) { return NonStructural{l.value / r.value}; } friend constexpr auto operator%(OpsReturnNonStructural l, OpsReturnNonStructural r) { return NonStructural{l.value % r.value}; } friend constexpr auto operator<<(OpsReturnNonStructural l, OpsReturnNonStructural r) { return NonStructural{l.value << r.value}; } friend constexpr auto operator>>(OpsReturnNonStructural l, OpsReturnNonStructural r) { return NonStructural{l.value >> r.value}; } friend constexpr auto operator&(OpsReturnNonStructural l, OpsReturnNonStructural r) { return NonStructural{l.value & r.value}; } friend constexpr auto operator|(OpsReturnNonStructural l, OpsReturnNonStructural r) { return NonStructural{l.value | r.value}; } friend constexpr auto operator^(OpsReturnNonStructural l, OpsReturnNonStructural r) { return NonStructural{l.value ^ r.value}; } friend constexpr auto operator&&(OpsReturnNonStructural l, OpsReturnNonStructural r) { return NonStructural{l.value && r.value}; } friend constexpr auto operator||(OpsReturnNonStructural l, OpsReturnNonStructural r) { return NonStructural{l.value || r.value}; } }; struct NoOps {}; template concept HasPlus = requires(L l, R r) { { l + r }; }; template concept HasMinus = requires(L l, R r) { { l - r }; }; template concept HasMultiply = requires(L l, R r) { { l * r }; }; template concept HasDivide = requires(L l, R r) { { l / r }; }; template concept HasModulo = requires(L l, R r) { { l % r }; }; template concept HasShiftLeft = requires(L l, R r) { { l << r }; }; template concept HasShiftRight = requires(L l, R r) { { l >> r }; }; template concept HasBitAnd = requires(L l, R r) { { l & r }; }; template concept HasBitOr = requires(L l, R r) { { l | r }; }; template concept HasBitXor = requires(L l, R r) { { l ^ r }; }; template concept HasLogicalAnd = requires(L l, R r) { { l && r }; }; template concept HasLogicalOr = requires(L l, R r) { { l || r }; }; template concept HasNoexceptPlus = requires(L l, R r) { { l + r } noexcept; }; template concept HasNoexceptMinus = requires(L l, R r) { { l - r } noexcept; }; template concept HasNoexceptMultiply = requires(L l, R r) { { l * r } noexcept; }; template concept HasNoexceptDivide = requires(L l, R r) { { l / r } noexcept; }; template concept HasNoexceptModulo = requires(L l, R r) { { l % r } noexcept; }; template concept HasNoexceptShiftLeft = requires(L l, R r) { { l << r } noexcept; }; template concept HasNoexceptShiftRight = requires(L l, R r) { { l >> r } noexcept; }; template concept HasNoexceptBitAnd = requires(L l, R r) { { l & r } noexcept; }; template concept HasNoexceptBitOr = requires(L l, R r) { { l | r } noexcept; }; template concept HasNoexceptBitXor = requires(L l, R r) { { l ^ r } noexcept; }; template concept HasNoexceptLogicalAnd = requires(L l, R r) { { l && r } noexcept; }; template concept HasNoexceptLogicalOr = requires(L l, R r) { { l || r } noexcept; }; // Concept checks for int + int operations static_assert(HasPlus, std::constant_wrapper<3>>); static_assert(HasMinus, std::constant_wrapper<3>>); static_assert(HasMultiply, std::constant_wrapper<3>>); static_assert(HasDivide, std::constant_wrapper<3>>); static_assert(HasModulo, std::constant_wrapper<3>>); static_assert(HasShiftLeft, std::constant_wrapper<1>>); static_assert(HasShiftRight, std::constant_wrapper<1>>); static_assert(HasBitAnd, std::constant_wrapper<3>>); static_assert(HasBitOr, std::constant_wrapper<3>>); static_assert(HasBitXor, std::constant_wrapper<3>>); static_assert(HasLogicalAnd, std::constant_wrapper<3>>); static_assert(HasLogicalOr, std::constant_wrapper<3>>); static_assert(HasNoexceptPlus, std::constant_wrapper<3>>); static_assert(HasNoexceptMinus, std::constant_wrapper<3>>); static_assert(HasNoexceptMultiply, std::constant_wrapper<3>>); static_assert(HasNoexceptDivide, std::constant_wrapper<3>>); static_assert(HasNoexceptModulo, std::constant_wrapper<3>>); static_assert(HasNoexceptShiftLeft, std::constant_wrapper<1>>); static_assert(HasNoexceptShiftRight, std::constant_wrapper<1>>); static_assert(HasNoexceptBitAnd, std::constant_wrapper<3>>); static_assert(HasNoexceptBitOr, std::constant_wrapper<3>>); static_assert(HasNoexceptBitXor, std::constant_wrapper<3>>); static_assert(HasNoexceptLogicalAnd, std::constant_wrapper<3>>); static_assert(HasNoexceptLogicalOr, std::constant_wrapper<3>>); // NoOps static_assert(!HasPlus, std::constant_wrapper>); static_assert(!HasMinus, std::constant_wrapper>); static_assert(!HasMultiply, std::constant_wrapper>); static_assert(!HasDivide, std::constant_wrapper>); static_assert(!HasModulo, std::constant_wrapper>); static_assert(!HasShiftLeft, std::constant_wrapper>); static_assert(!HasShiftRight, std::constant_wrapper>); static_assert(!HasBitAnd, std::constant_wrapper>); static_assert(!HasBitOr, std::constant_wrapper>); static_assert(!HasBitXor, std::constant_wrapper>); static_assert(!HasLogicalAnd, std::constant_wrapper>); static_assert(!HasLogicalOr, std::constant_wrapper>); // Concept checks for WithOps operations static_assert(HasNoexceptPlus, std::constant_wrapper>); static_assert(HasNoexceptMinus, std::constant_wrapper>); static_assert(HasNoexceptMultiply, std::constant_wrapper>); static_assert(HasNoexceptDivide, std::constant_wrapper>); static_assert(HasNoexceptModulo, std::constant_wrapper>); static_assert(HasNoexceptShiftLeft, std::constant_wrapper>); static_assert(HasNoexceptShiftRight, std::constant_wrapper>); static_assert(HasNoexceptBitAnd, std::constant_wrapper>); static_assert(HasNoexceptBitOr, std::constant_wrapper>); static_assert(HasNoexceptBitXor, std::constant_wrapper>); static_assert(HasNoexceptLogicalAnd, std::constant_wrapper>); static_assert(HasNoexceptLogicalOr, std::constant_wrapper>); // clang-format off // Non-structural types use implicit conversion to underlying type static_assert(HasPlus, std::constant_wrapper>); static_assert(HasMinus, std::constant_wrapper>); static_assert(HasMultiply, std::constant_wrapper>); static_assert(HasDivide, std::constant_wrapper>); static_assert(HasModulo, std::constant_wrapper>); static_assert(HasShiftLeft, std::constant_wrapper>); static_assert(HasShiftRight, std::constant_wrapper>); static_assert(HasBitAnd, std::constant_wrapper>); static_assert(HasBitOr, std::constant_wrapper>); static_assert(HasBitXor, std::constant_wrapper>); static_assert(HasLogicalAnd, std::constant_wrapper>); static_assert(HasLogicalOr, std::constant_wrapper>); static_assert(!HasNoexceptPlus, std::constant_wrapper>); static_assert(!HasNoexceptMinus, std::constant_wrapper>); static_assert(!HasNoexceptMultiply, std::constant_wrapper>); static_assert(!HasNoexceptDivide, std::constant_wrapper>); static_assert(!HasNoexceptModulo, std::constant_wrapper>); static_assert(!HasNoexceptShiftLeft, std::constant_wrapper>); static_assert(!HasNoexceptShiftRight, std::constant_wrapper>); static_assert(!HasNoexceptBitAnd, std::constant_wrapper>); static_assert(!HasNoexceptBitOr, std::constant_wrapper>); static_assert(!HasNoexceptBitXor, std::constant_wrapper>); static_assert(!HasNoexceptLogicalAnd, std::constant_wrapper>); static_assert(!HasNoexceptLogicalOr, std::constant_wrapper>); // clang-format on constexpr bool test() { { // int + int std::constant_wrapper<6> cw6; std::constant_wrapper<3> cw3; std::same_as> decltype(auto) result = cw6 + cw3; static_assert(result == 9); std::same_as> decltype(auto) result2 = cw6 - cw3; static_assert(result2 == 3); std::same_as> decltype(auto) result3 = cw6 * cw3; static_assert(result3 == 18); std::same_as> decltype(auto) result4 = cw6 / cw3; static_assert(result4 == 2); std::same_as> decltype(auto) result5 = cw6 % cw3; static_assert(result5 == 0); std::same_as> decltype(auto) result6 = cw6 & cw3; static_assert(result6 == 2); std::same_as> decltype(auto) result7 = cw6 | cw3; static_assert(result7 == 7); std::same_as> decltype(auto) result8 = cw6 ^ cw3; static_assert(result8 == 5); // Shift operations: 6 << 3 = 48, 6 >> 3 = 0 std::same_as> decltype(auto) result9 = cw6 << cw3; static_assert(result9 == 48); std::same_as> decltype(auto) result10 = cw6 >> cw3; static_assert(result10 == 0); // logical operations: int convertible to bool, so constant_wrapper overload is disabled // They are implicitly converted to bool and use built-in operators, resulting in a bool std::same_as decltype(auto) result11 = cw6 && cw3; assert(result11 == true); std::constant_wrapper<0> cw0; std::same_as decltype(auto) result12 = cw0 || cw3; assert(result12 == true); } { // WithOps operations std::constant_wrapper cwWithOps6; std::constant_wrapper cwWithOps3; std::same_as> decltype(auto) result = cwWithOps6 + cwWithOps3; static_assert(result.value.value == 9); std::same_as> decltype(auto) result2 = cwWithOps6 - cwWithOps3; static_assert(result2.value.value == 3); std::same_as> decltype(auto) result3 = cwWithOps6 * cwWithOps3; static_assert(result3.value.value == 18); std::same_as> decltype(auto) result4 = cwWithOps6 / cwWithOps3; static_assert(result4.value.value == 2); std::same_as> decltype(auto) result5 = cwWithOps6 % cwWithOps3; static_assert(result5.value.value == 0); std::same_as> decltype(auto) result6 = cwWithOps6 & cwWithOps3; static_assert(result6.value.value == 2); std::same_as> decltype(auto) result7 = cwWithOps6 | cwWithOps3; static_assert(result7.value.value == 7); std::same_as> decltype(auto) result8 = cwWithOps6 ^ cwWithOps3; static_assert(result8.value.value == 5); // Shift operations: 6 << 3 = 48, 6 >> 3 = 0 std::same_as> decltype(auto) result9 = cwWithOps6 << cwWithOps3; static_assert(result9.value.value == 48); std::same_as> decltype(auto) result10 = cwWithOps6 >> cwWithOps3; static_assert(result10.value.value == 0); std::same_as> decltype(auto) result11 = cwWithOps6 && cwWithOps3; static_assert(result11.value.value == 1); std::same_as> decltype(auto) result12 = cwWithOps6 || cwWithOps3; static_assert(result12.value.value == 1); } { // Non-structural return types use implicit conversion std::constant_wrapper cwOpt6; std::constant_wrapper cwOpt3; std::same_as decltype(auto) result = cwOpt6 + cwOpt3; assert(result.get() == 9); std::same_as decltype(auto) result2 = cwOpt6 - cwOpt3; assert(result2.get() == 3); std::same_as decltype(auto) result3 = cwOpt6 * cwOpt3; assert(result3.get() == 18); std::same_as decltype(auto) result4 = cwOpt6 / cwOpt3; assert(result4.get() == 2); std::same_as decltype(auto) result5 = cwOpt6 % cwOpt3; assert(result5.get() == 0); std::same_as decltype(auto) result6 = cwOpt6 & cwOpt3; assert(result6.get() == 2); std::same_as decltype(auto) result7 = cwOpt6 | cwOpt3; assert(result7.get() == 7); std::same_as decltype(auto) result8 = cwOpt6 ^ cwOpt3; assert(result8.get() == 5); // Shift operations: 6 << 3 = 48, 6 >> 3 = 0 std::same_as decltype(auto) result9 = cwOpt6 << cwOpt3; assert(result9.get() == 48); std::same_as decltype(auto) result10 = cwOpt6 >> cwOpt3; assert(result10.get() == 0); std::same_as decltype(auto) result11 = cwOpt6 && cwOpt3; assert(result11.get() == 1); std::same_as decltype(auto) result12 = cwOpt6 || cwOpt3; assert(result12.get() == 1); } { // Mix with runtime param: these operators are not used std::constant_wrapper<6> cw6; int i = 3; std::same_as decltype(auto) result = cw6 + i; assert(result == 9); std::same_as decltype(auto) result2 = cw6 - i; assert(result2 == 3); std::same_as decltype(auto) result3 = cw6 * i; assert(result3 == 18); std::same_as decltype(auto) result4 = cw6 / i; assert(result4 == 2); std::same_as decltype(auto) result5 = cw6 % i; assert(result5 == 0); std::same_as decltype(auto) result6 = cw6 & i; assert(result6 == 2); std::same_as decltype(auto) result7 = cw6 | i; assert(result7 == 7); std::same_as decltype(auto) result8 = cw6 ^ i; assert(result8 == 5); // Shift operations: 6 << 3 = 48, 6 >> 3 = 0 std::same_as decltype(auto) result9 = cw6 << i; assert(result9 == 48); std::same_as decltype(auto) result10 = cw6 >> i; assert(result10 == 0); std::same_as decltype(auto) result11 = cw6 && i; assert(result11 == true); std::constant_wrapper<0> cw0; std::same_as decltype(auto) result12 = cw0 || i; assert(result12 == true); } { // with integral_constant std::constant_wrapper<6> cw6; std::integral_constant ic3; std::same_as> decltype(auto) result = cw6 + ic3; static_assert(result == 9); std::same_as> decltype(auto) result2 = cw6 - ic3; static_assert(result2 == 3); std::same_as> decltype(auto) result3 = cw6 * ic3; static_assert(result3 == 18); std::same_as> decltype(auto) result4 = cw6 / ic3; static_assert(result4 == 2); std::same_as> decltype(auto) result5 = cw6 % ic3; static_assert(result5 == 0); std::same_as> decltype(auto) result6 = cw6 & ic3; static_assert(result6 == 2); std::same_as> decltype(auto) result7 = cw6 | ic3; static_assert(result7 == 7); std::same_as> decltype(auto) result8 = cw6 ^ ic3; static_assert(result8 == 5); // Shift operations: 6 << 3 = 48, 6 >> 3 = 0 std::same_as> decltype(auto) result9 = cw6 << ic3; static_assert(result9 == 48); std::same_as> decltype(auto) result10 = cw6 >> ic3; static_assert(result10 == 0); // logical operations: int convertible to bool, so constant_wrapper overload is disabled // They are implicitly converted to bool and use built-in operators, resulting in a bool std::same_as decltype(auto) result11 = cw6 && ic3; assert(result11 == true); std::constant_wrapper<0> cw0; std::same_as decltype(auto) result12 = cw0 || ic3; assert(result12 == true); } return true; } int main(int, char**) { test(); static_assert(test()); return 0; }