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

### 2017. `std::reference_wrapper` makes incorrect usage of `std::result_of`

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

Priority: Not Prioritized

View all other issues in [refwrap].

View all issues with C++11 status.

Discussion:

`std::reference_wrapper`'s function call operator uses wrong type encoding for rvalue-arguments. An rvalue-argument of type `T` must be encoded as `T&&`, not as just `T`.

>     #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 template `f1` (which is similar to `std::reference_wrapper`'s function call operator). In the invocation (1) `F` is deduced as '`Functor`' and `Types` is deduced as type sequence which consists of one type '`std::string const`'. After the substitution we 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-level cv-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));
>     }

Let `r` be an rvalue of type '`std::string`' and `cr` be an rvalue of type '`std::string const`'. The expression `Str("1")` is `cr`. The corresponding return 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 the corresponding return type for the invocation `Functor().operator()(r)`, i.e. it is '`std::string &&`'. As a consequence, we have wrong reference binding in the return statement in `f1`.

Now lets consider the invocation (2) of the function template `f2`. When the 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 the standard, it would be a shame if it can't be fixed.

In the following example one would expect `f(Str("1"))` and `std::ref(f)(Str("2"))` to be equivalent but the current wording makes the invocation through `reference_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 of `result_of` within `reference_wrapper`'s function call operator: According to 22.10.6.5 [refwrap.invoke] p. 1 the invokable entity of type `T` is provided as lvalue, but `result_of` is fed as if it were an rvalue. This does not only lead to potentially incorrect result types, but it will also have the effect that we could never use the function call operator with a function type, because the type encoding used in `result_of` would form an invalid function type return a function type. The following program demonstrates this problem:

>     #include <functional>

>     void foo(int) {}

>     int main()
>     {
>        std::ref(foo)(0);  // error
>     }

The correct solution is to ensure that `T` becomes `T&` within `result_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&&... )>::type
   >     operator()(ArgTypes&&... args) const;

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