Movatterモバイル変換


[0]ホーム

URL:



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

2017.std::reference_wrapper makes incorrect usage ofstd::result_of

Section: 22.10.6[refwrap]Status:C++11Submitter: Nikolay IvchenkovOpened: 2010-11-15Last modified: 2016-01-28

Priority:Not Prioritized

View all otherissues in [refwrap].

View all issues withC++11 status.

Discussion:

std::reference_wrapper's function call operator useswrongtype encoding for rvalue-arguments. An rvalue-argument of typeT mustbe encoded asT&&, not as justT.

#include <functional>#include <iostream>#include <string>#include <type_traits>#include <utility>template <class F, class... Types>     typename std::result_of<F (Types...)>::type         f1(F f, Types&&... params){     return f(std::forward<Types...>(params...));}template <class F, class... Types>     typename std::result_of<F (Types&&...)>::type         f2(F f, Types&&... params){     return f(std::forward<Types...>(params...));}struct Functor{     template <class T>         T&& operator()(T&& t) const     {         return static_cast<T&&>(t);     }};int main(){     typedef std::string const Str;     std::cout << f1(Functor(), Str("1")) << std::endl; // (1)     std::cout << f2(Functor(), Str("2")) << std::endl; // (2)}

Lets consider the function templatef1 (which is similar tostd::reference_wrapper's function call operator). In the invocation(1)F is deduced as 'Functor' andTypes is deduced as type sequencewhich consists of one type 'std::string const'. After the substitutionwe have the following equivalent:

template <>    std::result_of<F (std::string const)>::type        f1<Functor, std::string const>(Functor f, std::string const && params){    return f(std::forward<const std::string>(params));}

The top-levelcv-qualifier in the parameter type of 'F (std::string const)' is removed, so we have

template <>    std::result_of<F (std::string)>::type        f1<Functor, std::string const>(Functor f, std::string const && params){    return f(std::forward<const std::string>(params));}

Letr be an rvalue of type 'std::string' andcr be an rvalue of type'std::string const'. The expressionStr("1") iscr. The correspondingreturn type for the invocation

Functor().operator()(r)

is 'std::string &&'. The corresponding return type for the invocation

Functor().operator()(cr)

is 'std::string const &&'.

std::result_of<Functor (std::string)>::type is the same type as thecorresponding return type for the invocationFunctor().operator()(r),i.e. it is 'std::string &&'. As a consequence, we have wrong referencebinding in the return statement inf1.

Now lets consider the invocation (2) of the function templatef2. Whenthe template arguments are substituted we have the following equivalent:

template <>    std::result_of<F (std::string const &&)>::type        f2<Functor, std::string const>(Functor f, std::string const && params){    return f(std::forward<const std::string>(params));}

std::result_of<F (std::string const &&)>::type is the same type as'std::string const &&'. This is correct result.

[2010-12-07 Jonathan Wakely comments and suggests a proposed resolution]

I agree with the analysis and I think this is a defect in thestandard, it would be a shame if it can't be fixed.

In the following example one would expectf(Str("1")) andstd::ref(f)(Str("2")) to be equivalent but the current wording makesthe invocation throughreference_wrapper ill-formed:

#include <functional>#include <string>struct Functor{   template <class T>       T&& operator()(T&& t) const       {           return static_cast<T&&>(t);       }};int main(){   typedef std::string const Str;   Functor f;   f( Str("1") );   std::ref(f)( Str("2") );  // error}

[2010-12-07 Daniel comments and refines the proposed resolution]

There is one further defect in the usage ofresult_of withinreference_wrapper's function call operator: According to 22.10.6.5[refwrap.invoke] p. 1the invokable entity of typeT is provided as lvalue, butresult_of is fed as if it were an rvalue. This does not only lead topotentially incorrect result types, but it will also have the effect thatwe could never use the function call operator with a function type,because the type encoding used inresult_of would form an invalidfunction type return a function type. The following program demonstratesthis problem:

#include <functional>void foo(int) {}int main(){   std::ref(foo)(0);  // error}

The correct solution is to ensure thatT becomesT&withinresult_of, which solves both problems at once.

[2011-02-24 Reflector discussion]

Moved to Tentatively Ready after 5 votes.

Proposed resolution:

  1. Change the synopsis in 22.10.6[refwrap] paragraph 1:

    namespace std {  template <class T> class reference_wrapper  {  public :    [...]    // invocation    template <class... ArgTypes>    typename result_of<T&(ArgTypes&&...)>::type    operator() (ArgTypes&&...) const;  };}
  2. Change the signature in 22.10.6.5[refwrap.invoke] before paragraph 1

    template <class... ArgTypes>typename result_of<T&(ArgTypes&&... )>::typeoperator()(ArgTypes&&... args) const;

    1Returns:INVOKE(get(), std::forward<ArgTypes>(args)...). (20.8.2)


[8]ページ先頭

©2009-2026 Movatter.jp