Issue 2774: std::function construction vs assignment (original) (raw)
This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++23 status.
2774. std::function construction vs assignment
Section: 22.10.17.3.2 [func.wrap.func.con] Status: C++23 Submitter: Barry Revzin Opened: 2016-09-14 Last modified: 2023-11-22
Priority: 3
View all other issues in [func.wrap.func.con].
View all issues with C++23 status.
Discussion:
I think there's a minor defect in the std::function interface. The constructor template is:
template function(F f);
while the assignment operator template is
template function& operator=(F&& f);
The latter came about as a result of LWG 1288(i), but that one was dealing with a specific issue that wouldn't have affected the constructor. I think the constructor should also take f by forwarding reference, this saves a move in the lvalue/xvalue cases and is also just generally more consistent. Should just make sure that it's stored as std::decay_t<F> instead of F.
Is there any reason to favor a by-value constructor over a forwarding-reference constructor?
[2019-07-26 Tim provides PR.]
Previous resolution [SUPERSEDED]:
This wording is relative to N4820.
- Edit 22.10.17.3 [func.wrap.func], class template
functionsynopsis, as indicated:namespace std {
template class function; // not defined
template<class R, class... ArgTypes> {
public:
using result_type = R;// 22.10.17.3.2 [func.wrap.func.con], construct/copy/destroy
function() noexcept;
function(nullptr_t) noexcept;
function(const function&);
function(function&&) noexcept;
template function(F&&);[…]
};[…]
}- Edit 22.10.17.3.2 [func.wrap.func.con] p7-11 as indicated:
template function(F&& f);
-7- Requires:LetFshall be Cpp17CopyConstructibleFDbedecay_t<F>.-8-
Remarks: This constructor template shall not participate in overload resolution unlessConstraints:F
- (8.1) —
is_same_v<FD, function>isfalse; and- (8.2) —
FDis Lvalue-Callable (22.10.17.3 [func.wrap.func]) for argument typesArgTypes...and return typeR.-?- Expects:
FDmeets the_Cpp17CopyConstructible_ requirements.-9- Ensures:
!*thisif any of the following hold:
- (9.1) —
fis a null function pointer value.- (9.2) —
fis a null member pointer value.- (9.3) —
Fis an instanceremove_cvref_t<F>is a specialization of thefunctionclass template, and!fistrue.-10- Otherwise,
*thistargetsa copy ofan object of typefFDdirect-non-list-initialized withstd::move(f)std::forward<F>(f). [Note: Implementations should avoid the use of dynamically allocated memory for small callable objects, for example, wherefisrefers to an object holding only a pointer or reference to an object and a member function pointer. — _end note_]-11- Throws:
ShallDoes not throw exceptions whenfFDis a function pointer type or a specialization ofreference_wrapper. Otherwise, may throw<T>for someTbad_allocor any exception thrown bythe initialization of the target object.F’s copy or move constructor
[2020-11-01; Daniel comments and improves the wording]
The proposed wording should — following the line of Marshall's "Mandating" papers — extract from the Cpp17CopyConstructible precondition a corresponding _Constraints:_element and in addition to that the wording should replace old-style elements such as _Expects:_by the recently agreed on elements.
See also the related issue LWG 3493(i).
Previous resolution [SUPERSEDED]:
This wording is relative to N4868.
- Edit 22.10.17.3 [func.wrap.func], class template
functionsynopsis, as indicated:namespace std {
template class function; // not definedtemplate<class R, class... ArgTypes> {
public:
using result_type = R;// 22.10.17.3.2 [func.wrap.func.con], construct/copy/destroy
function() noexcept;
function(nullptr_t) noexcept;
function(const function&);
function(function&&) noexcept;
template function(F&&);[…]
};[…]
}- Edit 22.10.17.3.2 [func.wrap.func.con] as indicated:
template function(F&& f);
Let
FDbedecay_t<F>.-8- Constraints:
- (8.1) —
is_same_v<remove_cvref_t<F>, function>isfalse,- (8.2) —
FDis Lvalue-Callable (22.10.17.3.1 [func.wrap.func.general]) for argument typesFArgTypes...and return typeR,- (8.3) —
is_copy_constructible_v<FD>istrue, and- (8.4) —
is_constructible_v<FD, F>istrue.-9- Preconditions:
~~F~~FDmeets the Cpp17CopyConstructible requirements.-10- Postconditions:
!*thisif any of the following hold:
- (10.1) —
fis a null function pointer value.- (10.2) —
fis a null member pointer value.- (10.3) —
Fis an instanceremove_cvref_t<F>is a specialization of the function class template, and!fistrue.-11- Otherwise,
*thistargetsa copy ofan object of typefFDdirect-non-list-initialized with~~std::move(f)~~std::forward<F>(f).-12- Throws: Nothing if
~~f~~FDis a specialization ofreference_wrapperor a function pointer type. Otherwise, may throwbad_allocor any exception thrown bythe initialization of the target object.F's copy or move constructor-13- Recommended practice: Implementations should avoid the use of dynamically allocated memory for small callable objects, for example, where
fisrefers to an object holding only a pointer or reference to an object and a member function pointer.
[2021-05-17; Tim comments and revises the wording]
The additional constraints added in the previous wording can induce constraint recursion, as noted in the discussion of LWG 3493(i). The wording below changes them to Mandates: instead to allow this issue to make progress independently of that issue.
The proposed resolution below has been implemented and tested on top of libstdc++.
[2021-05-20; Reflector poll]
Set status to Tentatively Ready after five votes in favour during reflector poll.
[2021-06-07 Approved at June 2021 virtual plenary. Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4885.
- Edit 22.10.17.3.1 [func.wrap.func.general], class template
functionsynopsis, as indicated:namespace std {
template class function; // not definedtemplate<class R, class... ArgTypes> {
public:
using result_type = R;// 22.10.17.3.2 [func.wrap.func.con], construct/copy/destroy
function() noexcept;
function(nullptr_t) noexcept;
function(const function&);
function(function&&) noexcept;
template function(F&&);[…]
};[…]
} - Edit 22.10.17.3.2 [func.wrap.func.con] as indicated:
template function(F&& f);
Let
FDbedecay_t<F>.-8- Constraints:
- (8.1) —
is_same_v<remove_cvref_t<F>, function>isfalse, and - (8.2) —
FDis Lvalue-Callable (22.10.17.3.1 [func.wrap.func.general]) for argument typesFArgTypes...and return typeR.
-?- Mandates:
- (?.1) —
is_copy_constructible_v<FD>istrue, and - (?.2) —
is_constructible_v<FD, F>istrue.
-9- Preconditions:
~~F~~FDmeets the Cpp17CopyConstructible requirements.-10- Postconditions:
!*thisistrueif any of the following hold:- (10.1) —
fis a null function pointer value. - (10.2) —
fis a null member pointer value. - (10.3) —
Fis an instanceremove_cvref_t<F>is a specialization of thefunctionclass template, and!fistrue.
-11- Otherwise,
*thistargetsa copy ofan object of typefFDdirect-non-list-initialized with~~std::move(f)~~std::forward<F>(f).-12- Throws: Nothing if
~~f~~FDis a specialization ofreference_wrapperor a function pointer type. Otherwise, may throwbad_allocor any exception thrown bythe initialization of the target object.F's copy or move constructor-13- Recommended practice: Implementations should avoid the use of dynamically allocated memory for small callable objects, for example, where
fisrefers to an object holding only a pointer or reference to an object and a member function pointer. - (8.1) —