Movatterモバイル変換


[0]ホーム

URL:



This page is a snapshot from the LWG issues list, see theLibrary Active Issues List for more information and the meaning ofResolved status.

815.std::function andreference_closure do not use perfect forwarding

Section: 22.10.17.3.5[func.wrap.func.inv]Status:ResolvedSubmitter: Alisdair MeredithOpened: 2008-03-16Last modified: 2016-01-28

Priority:Not Prioritized

View all otherissues in [func.wrap.func.inv].

View all issues withResolved status.

Discussion:

std::function andreference_closure should use "perfect forwarding" asdescribed in the rvalue core proposal.

[Sophia Antipolis:]

According to Doug Gregor, as far asstd::function is concerned, perfectforwarding can not be obtained because of type erasure. Not everyoneagreed with this diagnosis of forwarding.

[2009-05-01 Howard adds:]

Sebastian Gesemann brought to my attention that theCopyConstructiblerequirement onfunction'sArgTypes... is an unnecessaryrestriction.

template<Returnable R,CopyConstructible... ArgTypes>class function<R(ArgTypes...)>...

On further investigation, this complaint seemed to be the sameissue as this one. I believe the reasonCopyConstructible was putonArgTypes in the first place was because of the nature of theinvoke member:

template<class R, class ...ArgTypes>Rfunction<R(ArgTypes...)>::operator()(ArgTypes... arg) const{    if (f_ == 0)        throw bad_function_call();    return (*f_)(arg...);}

However now with rvalue-refs, "by value" no longer impliesCopyConstructible(as Sebastian correctly points out). If rvalue arguments are supplied,MoveConstructibleis sufficient. Furthermore, the constraint need not be applied infunctionif I understand correctly. Rather the client must apply the proper constraintsat the call site. Therefore, at the very least, I recommend thatCopyConstructiblebe removed from the template classfunction.

Furthermore we need to mandate that theinvoker is coded as:

template<class R, class ...ArgTypes>Rfunction<R(ArgTypes...)>::operator()(ArgTypes... arg) const{    if (f_ == 0)        throw bad_function_call();    return (*f_)(std::forward<ArgTypes>(arg)...);}

Note thatArgTypes&& (the "perfect forwarding signature") is not appropriate here as this is not a deduced context forArgTypes. Insteadthe client's arguments must implicitly convert to the non-deducedArgTypetype. Catching these arguments by value makes sense to enable decay.

Nextforward is used to move theArgTypes as efficiently aspossible, and also with minimum requirements (notCopyConstructible)to the type-erased functor. For object types, this will be amove. Forreference typeArgTypes, this will be a copy. The end resultmust bethat the following is a valid program:

#include <functional>#include <memory>#include <cassert>std::unique_ptr<int>f(std::unique_ptr<int> p, int& i){    ++i;    return std::move(p);}int main(){    int i = 2;    std::function<std::unique_ptr<int>(std::unique_ptr<int>,                                       int&> g(f);    std::unique_ptr<int> p = g(std::unique_ptr<int>(new int(1)), i);    assert(*p == 1);    assert(i == 3);}

[Tested in pre-concepts rvalue-ref-enabled compiler.]

In the example above, the firstArgType isunique_ptr<int>and the secondArgType isint&. Bothmust work!

[2009-05-27 Daniel adds:]

in the 2009-05-01 comment of above mentioned issue Howard

  1. Recommends to replace theCopyConstructible requirement by aMoveConstructible requirement
  2. Says: "Furthermore, the constraint need not be applied infunction if Iunderstand correctly. Rather the client must apply the proper constraintsat the call site"

I'm fine with (a), but I think comment (b) is incorrect, at least in thesense I read these sentences. Let's look at Howard's example code:

function<R(ArgTypes...)>::operator()(ArgTypes... arg) const{   if (f_ == 0)       throw bad_function_call();   return (*f_)(std::forward<ArgTypes>(arg)...);}

In the constrained scope of thisoperator() overload the expression"(*f_)(std::forward<ArgTypes>(arg)...)" must be valid. How can itdo so, ifArgTypes aren't at leastMoveConstructible?

[2009-07 Frankfurt:]

Leave this open and wait until concepts are removed from the WorkingDraft so that we know how to write the proposed resolution in terms ofdiffs to otherwise stable text.

[2009-10 Santa Cruz:]

Leave as open. Howard to provide wording. Howard welcomes any help.

[2009-12-12 Jonathan Wakely adds:]

22.10.17.3[func.wrap.func] says

2 A function objectf of typeF is Callable for argumenttypesT1, T2, ..., TN inArgTypes and a return typeR, if, given lvaluest1, t2, ..., tN of typesT1, T2, ...,TN, respectively,INVOKE (f, t1, t2, ..., tN) is well formed(20.7.2) and, ifR is notvoid, convertible toR.

N.B. lvalues, which means you can't usefunction<R(T&&)>orfunction<R(unique_ptr<T>)>

I recently implemented rvalue arguments in GCC'sstd::function, allthat was needed was to usestd::forward<ArgTypes> in a fewplaces. The example in issue 815 works.

I think 815 could be resolved by removing the requirement that the targetfunction be callable with lvalues. SayingArgTypes need to beCopyConstructible is wrong, and IMHO sayingMoveConstructibleis unnecessary, since the by-value signature implies that already, but if it isneeded it should only be onoperator(), not the whole class (you couldin theory instantiatestd::function<R(noncopyable)> as long asyou don't invoke the call operator.)

I think defining invocation in terms ofINVOKE already implies perfectforwarding, so we don't need to say explicitly thatstd::forward shouldbe used (N.B. the types that are forwarded are those inArgTypes, whichcan differ from the actual parameter types of the target function. The actualparameter types have gone via type erasure, but that's not a problem - IMHOforwarding the arguments asArgTypes is the right thing to do anyway.)

Is it sufficient to simply replace "lvalues" with "values"? or do we need to saysomething like "lvalues whenTi is an lvalue-reference and rvaluesotherwise"? I prefer the former, so I propose the following resolution for 815:

Edit 22.10.17.3[func.wrap.func] paragraph 2:

2 A function objectf of typeF is Callable for argumenttypesT1, T2, ..., TN inArgTypes and a return typeR, if, givenlvaluest1, t2, ..., tN of typesT1, T2, ..., TN, respectively,INVOKE (f, t1, t2, ..., tN) iswell formed (20.7.2) and, ifR is notvoid, convertible toR.

[2009-12-12 Daniel adds:]

I don't like the reduction to "values" and prefer the alternative solutionsuggested using "lvalues when Ti is an lvalue-reference and rvalues otherwise".The reason why I dislike the shorter version is based on different usages of"values" as part of defining the semantics of requirement tables viaexpressions. E.g. 16.4.4.2[utility.arg.requirements]/1 says "a,b, andc are values of typeconst T;" or similar in23.2.2[container.requirements.general]/4 or /14 etc. My current readingof all these parts is thatboth rvalues and lvalues are required to besupported, but this interpretation would violate the intention of the suggestedfix of #815, if I correctly understand Jonathan's rationale.

[2009-12-12 Howard adds:]

"lvalues when Ti is an lvalue-reference and rvalues otherwise"

doesn't quite work here because theTi aren't deduced. They arespecified by thefunction type.Ti might beconstint& (an lvalue reference) and a validti might be2(a non-const rvalue). I've taken another stab at the wording using"expressions" and "bindable to".

[2010-02-09 Wording updated by Jonathan, Ganesh and Daniel.]

[2010-02-09 Moved to Tentatively Ready after 5 positive votes on c++std-lib.]

[2010-02-10 Daniel opens to improve wording.]

[2010-02-11 This issue is now addressed by870(i).]

[2010-02-12 Moved to Tentatively NAD Editorial after 5 positive votes onc++std-lib. Rationale added below.]

Rationale:

Addressed by870(i).

Proposed resolution:

Edit 22.10.17.3[func.wrap.func] paragraph 2:

2 A function objectf of typeF is Callable for argumenttypesT1, T2, ..., TN inArgTypes andareturn typeR, if, given lvaluest1, t2, ...,tN of typesT1, T2, ..., TN, respectively,theexpressionINVOKE(f,declval<ArgTypes>()...,Rt1, t2, ..., tN), considered as an unevaluatedoperand (7[expr]), is well formed (20.7.2) and, ifR is notvoid, convertible toR.


[8]ページ先頭

©2009-2026 Movatter.jp