Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 380
xskxzr Avatar asked Jan 04 '18 09:01

xskxzr


1 Answers

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.

like image 130
Drax Avatar answered Sep 29 '22 19:09

Drax