What's the intention of forward-by-lvalue-reference constructor while a perfect forwarding constructor exists?

可紊 提交于 2019-12-08 17:38:14

问题


Let's take std::pair<T1, T2> as an example. It has the following two constructors:

constexpr pair( const T1& x, const T2& y );                      // #1
template< class U1, class U2 > constexpr pair( U1&& x, U2&& y ); // #2

It seems that #2 can handle all cases that #1 can handle (without worse performance), except for cases where an argument is a list-initializer. For example,

std::pair<int, int> p({0}, {0}); // ill-formed without #1

So my question is:

  • If #1 is only intended for list-initializer argument, since x and y finally bind to temporary objects initialized from list-initializers, why not use constexpr pair( T1&& x, T2&& y ); instead?

  • Otherwise, what's the actual intention of #1?


回答1:


What if the object you want to store is a temporary one but is not movable ?

#include <type_traits>
#include <utility>
#include <iostream>

class   test
{
public:
  test() { std::cout << "ctor" << std::endl; }
  test(const test&) { std::cout << "copy ctor" << std::endl; }
  test(test&&) = delete; // { std::cout << "move ctor" << std::endl; }
  ~test() { std::cout << "dtor" << std::endl; }

private:
  int dummy;
};

template <class T1, class T2>
class   my_pair
{
public:
  my_pair() {}
  // Uncomment me plz !
  //my_pair(const T1& x, const T2& y) : first(x), second(y) {}
  template <class U1, class U2, class = typename std::enable_if<std::is_convertible<U1, T1>::value && std::is_convertible<U2, T2>::value>::type>
  my_pair(U1&& x, U2&& y) : first(std::forward<U1>(x)), second(std::forward<U2>(y)) {}

public:
  T1 first;
  T2 second;
};

int     main()
{
  my_pair<int, test>    tmp(5, test());
}

The above code doesn't compile because the so called "perfect" forwarding constructor of my_pair forwards the temporary test object as an rvalue reference which in turn tries to call the explicitly deleted move constructor of test.

If we remove the comment from my_pair's not so "perfect" constructor it is preferred by overload resolution and basically forces the copy of the temporary test object and thus makes it work.




回答2:


You answered your own question.

pair( const T1& x, const T2& y ); was there before the forwarding constructor was added in C++17. The constexpr was added in C++14. Now why keep the constructor around? Two reasons: 1) backwards compatibility and 2) because as you said, perfectly well-formed code would refuse to compile. C++ has a heavy emphasis on backwards compatibility and takes a very conservative approach to deprecating code because it can break in unexpected ways.

Now if you want a more realistic example, try std::for_each. You can change it to perfect forward a functor without any breakage in code. However, there's little incentive to add this change because it's specified to make only exactly one copy. In other words, the change is not very high priority.



来源:https://stackoverflow.com/questions/48092023/whats-the-intention-of-forward-by-lvalue-reference-constructor-while-a-perfect

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