SERVER-126153 unique_function c++20 updates (#53353)

GitOrigin-RevId: a4308f8e64a94aabbd3dec11f167270c5aa49533
This commit is contained in:
Billy Donahue 2026-05-21 12:49:25 -04:00 committed by MongoDB Bot
parent f686b54e2b
commit edc5fa4a70
2 changed files with 29 additions and 64 deletions

View File

@ -161,24 +161,9 @@ class unique_function;
*/
template <typename RetType, typename... Args>
class unique_function<RetType(Args...)> {
private:
// `TagTypeBase` is used as a base for the `TagType` type, to prevent it from being an
// aggregate.
struct TagTypeBase {
protected:
TagTypeBase() = default;
};
// `TagType` is used as a placeholder type in parameter lists for `enable_if` clauses. They
// have to be real parameters, not template parameters, due to MSVC limitations.
class TagType : TagTypeBase {
TagType() = default;
friend unique_function;
};
public:
using result_type = RetType;
~unique_function() noexcept = default;
unique_function() = default;
unique_function(const unique_function&) = delete;
@ -189,7 +174,7 @@ public:
void swap(unique_function& that) noexcept {
using std::swap;
swap(this->impl, that.impl);
swap(_impl, that._impl);
}
friend void swap(unique_function& a, unique_function& b) noexcept {
@ -200,30 +185,20 @@ public:
// `void *` accepting function object. This will permit reusing the core impl object when
// converting between related function types, such as
// `int (std::string)` -> `void (const char *)`
template <typename Functor>
/* implicit */
unique_function(
Functor&& functor,
// The remaining arguments here are only for SFINAE purposes to enable this ctor when our
// requirements are met. They must be concrete parameters not template parameters to work
// around bugs in some compilers that we presently use. We may be able to revisit this
// design after toolchain upgrades for C++17.
std::enable_if_t<std::is_invocable_r<RetType, Functor, Args...>::value, TagType> =
makeTag(),
std::enable_if_t<std::is_move_constructible<Functor>::value, TagType> = makeTag(),
std::enable_if_t<!std::is_same<std::decay_t<Functor>, unique_function>::value, TagType> =
makeTag())
: impl(makeImpl(std::forward<Functor>(functor))) {}
template <typename F>
requires(!std::same_as<std::decay_t<F>, unique_function> &&
std::is_invocable_r_v<RetType, F, Args...> && std::move_constructible<F>)
explicit(false) unique_function(F&& f) : _impl(_makeImpl(std::forward<F>(f))) {}
unique_function(std::nullptr_t) noexcept {}
explicit(false) unique_function(std::nullptr_t) noexcept {}
RetType operator()(Args... args) const {
invariant(static_cast<bool>(*this));
return impl->call(std::forward<Args>(args)...);
return _impl->call(std::forward<Args>(args)...);
}
explicit operator bool() const noexcept {
return static_cast<bool>(this->impl);
return static_cast<bool>(_impl);
}
// Needed to make `std::is_convertible<mongo::unique_function<...>, std::function<...>>` be
@ -240,22 +215,20 @@ public:
template <typename Signature>
operator std::function<Signature>() const = delete;
private:
// The `TagType` type cannot be constructed as a default function-parameter in Clang. So we use
// a static member function that initializes that default parameter.
static TagType makeTag() {
return {};
bool operator==(std::nullptr_t) const noexcept {
return !*this;
}
private:
struct Impl {
virtual ~Impl() noexcept = default;
virtual ~Impl() = default;
virtual RetType call(Args&&... args) = 0;
};
template <typename Functor>
static auto makeImpl(Functor&& functor) {
template <typename F>
static auto _makeImpl(F&& f) {
struct SpecificImpl : Impl {
explicit SpecificImpl(Functor&& func) : f(std::forward<Functor>(func)) {}
explicit SpecificImpl(F&& f) : f(std::forward<F>(f)) {}
RetType call(Args&&... args) override {
if constexpr (std::is_void_v<RetType>) {
@ -267,13 +240,13 @@ private:
}
}
std::decay_t<Functor> f;
std::decay_t<F> f;
};
return std::make_unique<SpecificImpl>(std::forward<Functor>(functor));
return std::make_unique<SpecificImpl>(std::forward<F>(f));
}
std::unique_ptr<Impl> impl;
std::unique_ptr<Impl> _impl;
};
namespace MONGO_MOD_FILE_PRIVATE functional_details {
@ -304,23 +277,4 @@ template <
typename Sig = typename functional_details::UFDeductionHelper<decltype(&T::operator())>::type>
unique_function(T) -> unique_function<Sig>;
template <typename Signature>
bool operator==(const unique_function<Signature>& lhs, std::nullptr_t) noexcept {
return !lhs;
}
template <typename Signature>
bool operator!=(const unique_function<Signature>& lhs, std::nullptr_t) noexcept {
return static_cast<bool>(lhs);
}
template <typename Signature>
bool operator==(std::nullptr_t, const unique_function<Signature>& rhs) noexcept {
return !rhs;
}
template <typename Signature>
bool operator!=(std::nullptr_t, const unique_function<Signature>& rhs) noexcept {
return static_cast<bool>(rhs);
}
} // namespace MONGO_MOD_PUB mongo

View File

@ -35,6 +35,8 @@
#include <string>
#include <boost/optional.hpp>
/**
* Note that tests in this file are deliberately outside the mongodb namespace to ensure that
* deduction works appropriately via adl. I.e. this set of tests doesn't follow our usual
@ -805,6 +807,15 @@ TEST(UniqueFunctionTest, functionDominanceExample) {
ASSERT_TRUE(accept(std::move(uf), nullptr));
}
TEST(UniqueFunctionTest, ConvertibilitySimple) {
using Muf = mongo::unique_function<int(int)>;
static_assert(!std::is_constructible_v<Muf, boost::optional<Muf>&&>);
auto lam = [](int) {
};
static_assert(std::is_constructible_v<mongo::unique_function<void(int)>, decltype(lam)&&>);
}
// Enable these tests to manually verify that we get warnings (which are promoted to errors).
// Note: because the warning is from inside the template instantiations, it usually won't show up
// with clangd, you need to do an actual build.