The implementation of std::forward

匿名 (未验证) 提交于 2019-12-03 02:14:01

问题:

I'm reading Overview of the New C++ (C++11/14) (PDF only), at Slide 288 it gives an implementation of std::forward:

template<typename T>                // For lvalues (T is T&), T&& std::forward(T&& param)         // take/return lvalue refs. {                                   // For rvalues (T is T),     return static_cast<T&&>(param); // take/return rvalue refs. } 

And then gives another implemention in text:

The usual std::forward implementation is:

template<typename T> struct identity {     typedef T type; }; template<typename T> T&& forward(typename identity<T>::type&& param) { return static_cast<identity<T>::type&&>(param); } 

What is the difference? Why is latter the usual implementation?

回答1:

The problem with the first is that you can write std::forward(x), which doesn't do what you want, since it always produces lvalue references.

The argument in the second case is a non-deduced context, preventing automatic deduction of the template argument. This forces you to write std::forward<T>(x), which is the right thing to do.

Also, the argument type for the second overload should be typename identity<T>::type& because the input to idiomatic use of std::forward is always an lvalue.

Edit: The standard actually mandates a signature equivalent to this one (which, incidentally, is exactly what libc++ has):

template <class T> T&& forward(typename remove_reference<T>::type& t) noexcept; template <class T> T&& forward(typename remove_reference<T>::type&& t) noexcept; 


回答2:

The implementation in libc++ uses std::remove_reference and two overloads. Here is the source (after removing some macros):

template <class T> inline T&& forward(typename std::remove_reference<T>::type& t) noexcept {     return static_cast<T&&>(t); }  template <class T> inline T&& forward(typename std::remove_reference<T>::type&& t) noexcept {     static_assert(!std::is_lvalue_reference<T>::value,                   "Can not forward an rvalue as an lvalue.");     return static_cast<T&&>(t); } 

but note that in C++14, std::forward is constexpr.



回答3:

The first case as Sebastian Redl said will always give you an lvalue reference. The reason is that an rvalue reference in the parameter would be passed as an lvalue reference, and the parameter T&& type is a universal reference rather than an rvalue reference.

Actually if the first case is correct, we don't even need forward any more. Here is an experiment to demonstrate how universal reference parameters are passed

template <typename T, typename U> void g(T&& t, U&& u) {     std::cout << "t is lvalue ref: "               << std::is_lvalue_reference<decltype(t)>::value << std::endl; // 1     std::cout << "t is rvalue ref: "               << std::is_rvalue_reference<decltype(t)>::value << std::endl; // 0     std::cout << "u is lvalue ref: "               << std::is_lvalue_reference<decltype(u)>::value << std::endl; // 1     std::cout << "u is rvalue ref: "               << std::is_rvalue_reference<decltype(u)>::value << std::endl; // 0 }  template <typename T, typename U> void f(T&& t, U&& u) {     std::cout << "t is lvalue ref: "               << std::is_lvalue_reference<decltype(t)>::value << std::endl; // 1     std::cout << "t is rvalue ref: "               << std::is_rvalue_reference<decltype(t)>::value << std::endl; // 0     std::cout << "u is lvalue ref: "               << std::is_lvalue_reference<decltype(u)>::value << std::endl; // 0     std::cout << "u is rvalue ref: "               << std::is_rvalue_reference<decltype(u)>::value << std::endl; // 1      g(t, u); }  int main() {     std::unique_ptr<int> t;     f(t, std::unique_ptr<int>());     return 0; } 

The program turns out that both t and u passed from f to g is lvalue references, despite that u is an rvalue reference in f. So in the first case the parameter of forward just doesn't have a chance to be an rvalue reference.

The identity is used to change the parameter type from universal reference to an rvalue reference (as mentioned by Redl, it's more precise to use std::remove_reference). However this change makes the template type deduction not possible any longer, so that the type parameter for forward is mandatory, as a result we shall write forward<T>(t).

But the second case in your question is not correct either, as also mentioned by Redl, the correct approach is an overload whose parameter is an lvalue reference.

The most straightforward implementation I can find is this

template <typename T> T&& forward(typename identity<T>::type& param) {     return static_cast<T&&>(param); } 

It works for universal references, for example

template <typename T, typename U> void f(T&& t, U&& u) {     ::forward<T>(t);     ::forward<U>(u); }  std::unique_ptr<int> t; f(t, std::unique_ptr<int>()); // deduction in f: //   T = unique_ptr&, decltype(t) = unique_ptr& //   U = unique_ptr, decltype(u) = unique_ptr&& (but treated as an lvalue reference) // specialization of forward: //   forward<T> = forward<unique_ptr&>, param type = unique_ptr& //                                      return type = unique_ptr& //   forward<U> = forward<unique_ptr>,  param type = unique_ptr& //                                      return type = unique_ptr&& 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!