Skip to content

Commit bb05875

Browse files
<functional>: bind_front() should use decayed types to determine its return type (#1293)
Fixes #1292. Co-authored-by: Stephan T. Lavavej <[email protected]>
1 parent 4386d74 commit bb05875

File tree

2 files changed

+79
-15
lines changed

2 files changed

+79
-15
lines changed

stl/inc/functional

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,24 +1557,18 @@ constexpr auto _Call_front_binder(index_sequence<_Ix...>, _Cv_FD&& _Obj, _Cv_tup
15571557
template <class _Fx, class... _Types>
15581558
class _Front_binder { // wrap bound callable object and arguments
15591559
private:
1560-
static_assert(is_constructible_v<decay_t<_Fx>, _Fx>,
1561-
"std::bind_front() requires the decayed callable to be constructible from an undecayed callable");
1562-
static_assert(is_move_constructible_v<decay_t<_Fx>>,
1563-
"std::bind_front() requires the decayed callable to be move constructible");
1564-
static_assert(conjunction_v<is_constructible<decay_t<_Types>, _Types>...>,
1565-
"std::bind_front() requires the decayed bound arguments to be constructible from undecayed bound arguments");
1566-
static_assert(conjunction_v<is_move_constructible<decay_t<_Types>>...>,
1567-
"std::bind_front() requires the decayed bound arguments to be move constructible");
1560+
using _Seq = index_sequence_for<_Types...>;
15681561

1569-
using _Seq = index_sequence_for<_Types...>;
1570-
using _First = decay_t<_Fx>;
1571-
using _Second = tuple<decay_t<_Types>...>;
1562+
_Compressed_pair<_Fx, tuple<_Types...>> _Mypair;
15721563

1573-
_Compressed_pair<_First, _Second> _Mypair;
1564+
_STL_INTERNAL_STATIC_ASSERT(is_same_v<_Fx, decay_t<_Fx>>);
1565+
_STL_INTERNAL_STATIC_ASSERT((is_same_v<_Types, decay_t<_Types>> && ...));
15741566

15751567
public:
1576-
constexpr explicit _Front_binder(_Fx&& _Func, _Types&&... _Args)
1577-
: _Mypair(_One_then_variadic_args_t{}, _STD forward<_Fx>(_Func), _STD forward<_Types>(_Args)...) {}
1568+
template <class _FxInit, class... _TypesInit,
1569+
enable_if_t<sizeof...(_TypesInit) != 0 || !is_same_v<remove_cvref_t<_FxInit>, _Front_binder>, int> = 0>
1570+
constexpr explicit _Front_binder(_FxInit&& _Func, _TypesInit&&... _Args)
1571+
: _Mypair(_One_then_variadic_args_t{}, _STD forward<_FxInit>(_Func), _STD forward<_TypesInit>(_Args)...) {}
15781572

15791573
template <class... _Unbound>
15801574
constexpr auto operator()(_Unbound&&... _Unbargs) & noexcept(noexcept(
@@ -1614,7 +1608,16 @@ public:
16141608
// FUNCTION TEMPLATE bind_front
16151609
template <class _Fx, class... _Types>
16161610
_NODISCARD constexpr auto bind_front(_Fx&& _Func, _Types&&... _Args) {
1617-
return _Front_binder<_Fx, _Types...>(_STD forward<_Fx>(_Func), _STD forward<_Types>(_Args)...);
1611+
static_assert(is_constructible_v<decay_t<_Fx>, _Fx>,
1612+
"std::bind_front requires the decayed callable to be constructible from an undecayed callable");
1613+
static_assert(is_move_constructible_v<decay_t<_Fx>>,
1614+
"std::bind_front requires the decayed callable to be move constructible");
1615+
static_assert(conjunction_v<is_constructible<decay_t<_Types>, _Types>...>,
1616+
"std::bind_front requires the decayed bound arguments to be constructible from undecayed bound arguments");
1617+
static_assert(conjunction_v<is_move_constructible<decay_t<_Types>>...>,
1618+
"std::bind_front requires the decayed bound arguments to be move constructible");
1619+
1620+
return _Front_binder<decay_t<_Fx>, decay_t<_Types>...>(_STD forward<_Fx>(_Func), _STD forward<_Types>(_Args)...);
16181621
}
16191622
#endif // _HAS_CXX20
16201623

tests/std/tests/P0356R5_bind_front/test.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,65 @@ int main() {
164164
auto bound7 = move(bound6);
165165
assert(*move(bound6)() == -9000);
166166
assert(*move(bound7)() == 1234);
167+
168+
// Also test GH-1292 "bind_front violates [func.require]p8" in which the return type of bind_front inadvertently
169+
// depends on the value category and/or cv-qualification of its arguments.
170+
{
171+
struct S {
172+
int i = 42;
173+
};
174+
S s;
175+
auto lambda = [](S x) { return x.i; };
176+
auto returns_lambda = [=] { return lambda; };
177+
auto returns_const_lambda = [=]() -> const decltype(lambda) { return lambda; };
178+
auto returns_const_S = []() -> const S { return {}; };
179+
180+
using T = decltype(bind_front(lambda, s));
181+
static_assert(is_same_v<decltype(bind_front(lambda, move(s))), T>);
182+
static_assert(is_same_v<decltype(bind_front(lambda, S{})), T>);
183+
184+
static_assert(is_same_v<decltype(bind_front(move(lambda), s)), T>);
185+
static_assert(is_same_v<decltype(bind_front(move(lambda), move(s))), T>);
186+
static_assert(is_same_v<decltype(bind_front(move(lambda), S{})), T>);
187+
188+
static_assert(is_same_v<decltype(bind_front(returns_lambda(), s)), T>);
189+
static_assert(is_same_v<decltype(bind_front(returns_lambda(), move(s))), T>);
190+
static_assert(is_same_v<decltype(bind_front(returns_lambda(), S{})), T>);
191+
192+
static_assert(is_same_v<decltype(bind_front(lambda, as_const(s))), T>);
193+
static_assert(is_same_v<decltype(bind_front(lambda, move(as_const(s)))), T>);
194+
static_assert(is_same_v<decltype(bind_front(lambda, returns_const_S())), T>);
195+
196+
static_assert(is_same_v<decltype(bind_front(move(lambda), as_const(s))), T>);
197+
static_assert(is_same_v<decltype(bind_front(move(lambda), move(as_const(s)))), T>);
198+
static_assert(is_same_v<decltype(bind_front(move(lambda), returns_const_S())), T>);
199+
200+
static_assert(is_same_v<decltype(bind_front(returns_lambda(), as_const(s))), T>);
201+
static_assert(is_same_v<decltype(bind_front(returns_lambda(), move(as_const(s)))), T>);
202+
static_assert(is_same_v<decltype(bind_front(returns_lambda(), returns_const_S())), T>);
203+
204+
static_assert(is_same_v<decltype(bind_front(as_const(lambda), s)), T>);
205+
static_assert(is_same_v<decltype(bind_front(as_const(lambda), move(s))), T>);
206+
static_assert(is_same_v<decltype(bind_front(as_const(lambda), S{})), T>);
207+
208+
static_assert(is_same_v<decltype(bind_front(move(as_const(lambda)), s)), T>);
209+
static_assert(is_same_v<decltype(bind_front(move(as_const(lambda)), move(s))), T>);
210+
static_assert(is_same_v<decltype(bind_front(move(as_const(lambda)), S{})), T>);
211+
212+
static_assert(is_same_v<decltype(bind_front(returns_const_lambda(), s)), T>);
213+
static_assert(is_same_v<decltype(bind_front(returns_const_lambda(), move(s))), T>);
214+
static_assert(is_same_v<decltype(bind_front(returns_const_lambda(), S{})), T>);
215+
216+
static_assert(is_same_v<decltype(bind_front(as_const(lambda), as_const(s))), T>);
217+
static_assert(is_same_v<decltype(bind_front(as_const(lambda), move(as_const(s)))), T>);
218+
static_assert(is_same_v<decltype(bind_front(as_const(lambda), returns_const_S())), T>);
219+
220+
static_assert(is_same_v<decltype(bind_front(move(as_const(lambda)), as_const(s))), T>);
221+
static_assert(is_same_v<decltype(bind_front(move(as_const(lambda)), move(as_const(s)))), T>);
222+
static_assert(is_same_v<decltype(bind_front(move(as_const(lambda)), returns_const_S())), T>);
223+
224+
static_assert(is_same_v<decltype(bind_front(returns_const_lambda(), as_const(s))), T>);
225+
static_assert(is_same_v<decltype(bind_front(returns_const_lambda(), move(as_const(s)))), T>);
226+
static_assert(is_same_v<decltype(bind_front(returns_const_lambda(), returns_const_S())), T>);
227+
}
167228
}

0 commit comments

Comments
 (0)