libstdc++: Fix std::bind_front perfect forwarding [PR111327]

In order to properly implement a perfect forwarding call wrapper (without
C++23 deducing 'this') we need a total of 8 operator() overloads, 4
enabled ones and 4 deleted ones, i.e. two for each const/ref qual pair,
as described in section 5.5 of P0847R6.  Otherwise the wrapper may not
do the right thing if the underlying function object has a deleted
const/ref-qualified operator() overload.  This patch fixes this issue in
std::bind_front.

	PR libstdc++/111327

libstdc++-v3/ChangeLog:

	* include/std/functional (_Bind_front::operator()): Add deleted
	fallback overloads for each const/ref qualifier pair.  Give the
	enabled overloads dummy constraints to make each one more
	specialized than the corresponding deleted overload.
	* testsuite/20_util/function_objects/bind_front/111327.cc: New test.
This commit is contained in:
Patrick Palka 2023-09-12 11:23:12 -04:00
parent f1e87aee5b
commit 4289f6ceef
2 changed files with 57 additions and 0 deletions

View file

@ -938,6 +938,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
~_Bind_front() = default;
template<typename... _CallArgs>
requires true
constexpr
invoke_result_t<_Fd&, _BoundArgs&..., _CallArgs...>
operator()(_CallArgs&&... __call_args) &
@ -948,6 +949,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename... _CallArgs>
requires true
constexpr
invoke_result_t<const _Fd&, const _BoundArgs&..., _CallArgs...>
operator()(_CallArgs&&... __call_args) const &
@ -959,6 +961,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename... _CallArgs>
requires true
constexpr
invoke_result_t<_Fd, _BoundArgs..., _CallArgs...>
operator()(_CallArgs&&... __call_args) &&
@ -969,6 +972,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename... _CallArgs>
requires true
constexpr
invoke_result_t<const _Fd, const _BoundArgs..., _CallArgs...>
operator()(_CallArgs&&... __call_args) const &&
@ -979,6 +983,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
std::forward<_CallArgs>(__call_args)...);
}
template<typename... _CallArgs>
void operator()(_CallArgs&&...) & = delete;
template<typename... _CallArgs>
void operator()(_CallArgs&&...) const & = delete;
template<typename... _CallArgs>
void operator()(_CallArgs&&...) && = delete;
template<typename... _CallArgs>
void operator()(_CallArgs&&...) const && = delete;
private:
using _BoundIndices = index_sequence_for<_BoundArgs...>;

View file

@ -0,0 +1,41 @@
// PR libstdc++/111327 - std::bind_front (and std::not_fn) doesn't always
// perfectly forward according to value category of the call wrapper object
// { dg-options "-std=gnu++20" }
// { dg-do compile { target c++20 } }
#include <functional>
#include <utility>
struct F {
void operator()(...) & = delete;
void operator()(...) const &;
};
struct G {
void operator()(...) && = delete;
void operator()(...) const &&;
};
int main() {
auto f0 = std::bind_front(F{});
f0(); // { dg-error "deleted" }
std::move(f0)();
std::as_const(f0)();
std::move(std::as_const(f0))();
auto g0 = std::bind_front(G{});
g0(); // { dg-error "deleted" }
std::move(g0)(); // { dg-error "deleted" }
std::move(std::as_const(g0))();
auto f1 = std::bind_front(F{}, 42);
f1(); // { dg-error "deleted" }
std::move(f1)();
std::as_const(f1)();
std::move(std::as_const(f1))();
auto g1 = std::bind_front(G{}, 42);
g1(); // { dg-error "deleted" }
std::move(g1)(); // { dg-error "deleted" }
std::move(std::as_const(g1))();
}