From 8cf25523c8871efe15bbaa130e42a7e3074fdd8d Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Thu, 1 Jan 2026 18:56:30 -0700 Subject: [PATCH] :sparkles: Add `unary_plus` Problem: - The standard does not provide a function object that calls unary `operator+`. This can be useful for conventional conversions, for example an overloaded `operator+` that converts an scoped enumeration to its underlying type. Solution: - Add `unary_plus` for this purpose. - `unary_plus` deduces its argument and provides `is_transparent` in accordance with standard practice. --- docs/functional.adoc | 9 +++++++++ include/stdx/functional.hpp | 17 +++++++++++++++++ test/CMakeLists.txt | 1 + test/functional.cpp | 30 ++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+) create mode 100644 test/functional.cpp diff --git a/docs/functional.adoc b/docs/functional.adoc index c6e0a8f..db56581 100644 --- a/docs/functional.adoc +++ b/docs/functional.adoc @@ -34,3 +34,12 @@ v.emplace(0, stdx::with_result_of{make_S}); // this constructs S in-place thanks `with_result_of` can help to achieve in-place construction, effectively by deferring evaluation of function arguments. + +=== `unary_plus` + +`unary_plus` is a function object like +https://en.cppreference.com/w/cpp/utility/functional/negate.html[`std::negate`], +except it calls (unary) `operator+` instead of `operator-`. + +It also has a specialization for `void` which is _transparent_ like that of +https://en.cppreference.com/w/cpp/utility/functional/negate_void.html[`std::negate`]. diff --git a/include/stdx/functional.hpp b/include/stdx/functional.hpp index 0af9055..5497af9 100644 --- a/include/stdx/functional.hpp +++ b/include/stdx/functional.hpp @@ -170,5 +170,22 @@ template constexpr auto bind_back(Args &&...args) { } #endif + +template struct unary_plus { + constexpr auto operator()(T const &arg) const -> decltype(+arg) { + return +arg; + } +}; + +template <> struct unary_plus { + using is_transparent = int; + + template + constexpr auto operator()(T &&arg) const + -> decltype(+std::forward(arg)) { + return +std::forward(arg); + } +}; + } // namespace v1 } // namespace stdx diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1d888ca..bcfa47d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -43,6 +43,7 @@ add_tests( default_panic for_each_n_args function_traits + functional intrusive_forward_list intrusive_list intrusive_list_properties diff --git a/test/functional.cpp b/test/functional.cpp new file mode 100644 index 0000000..c013f3c --- /dev/null +++ b/test/functional.cpp @@ -0,0 +1,30 @@ +#include + +#include + +#include + +namespace { +template +constexpr auto detect_is_transparent = false; +template +constexpr auto + detect_is_transparent> = true; + +struct S {}; +constexpr auto operator+(S) { return 17; } +} // namespace + +TEST_CASE("unary_plus", "[functional]") { + STATIC_REQUIRE(stdx::unary_plus{}(17) == 17); + STATIC_REQUIRE(stdx::unary_plus<>{}(17) == 17); +} + +TEST_CASE("unary_plus transparency", "[functional]") { + STATIC_REQUIRE(not detect_is_transparent>); + STATIC_REQUIRE(detect_is_transparent>); +} + +TEST_CASE("unary_plus calls operator+", "[functional]") { + STATIC_REQUIRE(stdx::unary_plus<>{}(S{}) == 17); +}