Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/functional.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,30 @@ NOTE: https://wg21.link/P2714[P2714] added the ability (in C\\++26) to use a
non-type template parameter for the bound function; this works for function
pointers in C++17, and also for lambda expressions in C++20 and beyond.

=== `safe_identity`

`safe_identity` is a function object (of type `safe_identity_t`) similar
in intent to
https://en.cppreference.com/w/cpp/utility/functional/identity.html[`std::identity`].
A call to `safe_identity` returns its argument unchanged. _However_, the vital
difference with `safe_identity` is in the value category of what is returned.

[source,cpp]
----
int x;
auto f() -> int;

decltype(auto) r1 = std::identity{}(x); // r1 has type int &
decltype(auto) r2 = std::identity{}(f()); // r2 has type int && - dangling!

decltype(auto) r3 = stdx::safe_identity(x); // r3 has type int &
decltype(auto) r4 = stdx::safe_identity(f()); // r4 has type int - no longer dangling
----

NOTE: In standard usage, the type is `std::identity` and we must instantiate it
to use it; in `stdx`, the type is `safe_identity_t` and `safe_identity` is a
`constexpr inline` variable of that type.

=== `with_result_of`

`with_result_of` is a class that can be used for lazy evaluation.
Expand Down
8 changes: 8 additions & 0 deletions include/stdx/functional.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,5 +187,13 @@ template <> struct unary_plus<void> {
}
};

constexpr inline struct safe_identity_t {
using is_transparent = void;

template <typename T>
constexpr auto operator()(T &&t) const -> decltype(auto) {
return T(std::forward<T>(t));
}
} safe_identity;
} // namespace v1
} // namespace stdx
61 changes: 61 additions & 0 deletions test/functional.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#include "detail/tuple_types.hpp"

#include <stdx/functional.hpp>

#include <catch2/catch_test_macros.hpp>

#include <type_traits>
#include <utility>

namespace {
template <typename T, typename = void>
Expand All @@ -28,3 +31,61 @@ TEST_CASE("unary_plus transparency", "[functional]") {
TEST_CASE("unary_plus calls operator+", "[functional]") {
STATIC_REQUIRE(stdx::unary_plus<>{}(S{}) == 17);
}

TEST_CASE("safe_identity returns unchanged argument", "[functional]") {
static_assert(stdx::safe_identity(17) == 17);
static_assert(stdx::safe_identity(move_only{17}).value == 17);
}

TEST_CASE("safe_identity (copy)", "[functional]") {
counter::reset();
counter c0{};
[[maybe_unused]] auto c1 = stdx::safe_identity(c0);
CHECK(counter::copies == 1);
}

TEST_CASE("safe_identity (move)", "[functional]") {
counter::reset();
counter c0{};
[[maybe_unused]] auto c1 = stdx::safe_identity(std::move(c0));
CHECK(counter::copies == 0);
CHECK(counter::moves == 1);
}

TEST_CASE("safe_identity transparency", "[functional]") {
STATIC_REQUIRE(detect_is_transparent<stdx::safe_identity_t>);
}

namespace {
template <typename T> auto declval() -> T;
}

TEST_CASE("safe_identity value categories", "[functional]") {
static_assert(
std::is_same_v<decltype(stdx::safe_identity(declval<int>())), int>);
static_assert(
std::is_same_v<decltype(stdx::safe_identity(declval<int &>())), int &>);
static_assert(
std::is_same_v<decltype(stdx::safe_identity(declval<int &&>())), int>);
}

TEST_CASE("safe_identity cvref categories", "[functional]") {
static_assert(
std::is_same_v<decltype(stdx::safe_identity(declval<int const &>())),
int const &>);
static_assert(
std::is_same_v<decltype(stdx::safe_identity(declval<int volatile &>())),
int volatile &>);
static_assert(std::is_same_v<decltype(stdx::safe_identity(
declval<int const volatile &>())),
int const volatile &>);
static_assert(
std::is_same_v<decltype(stdx::safe_identity(declval<int const &&>())),
int>);
static_assert(
std::is_same_v<
decltype(stdx::safe_identity(declval<int volatile &&>())), int>);
static_assert(std::is_same_v<decltype(stdx::safe_identity(
declval<int const volatile &&>())),
int>);
}