-1

I am currently quite puzzled with C++17`s guaranteed RVO and its implications. I understand that for NRVO to kick in, I need to make sure that

  1. to return one and the same instance of an object through all possible return path of the function and

  2. to initialize an associated object with the function call on the call side

Consider a most simple decorated class Widget and I want to allocate a pair of Widgets without copies and then return it

#include<iostream>
#include<utility>

struct Widget {
  Widget() { std::cerr << "Default ctor" << std::endl; }
  explicit Widget(int i) { std::cerr << "custom ctor" << std::endl; }
  Widget(Widget const &) { std::cerr << "copy ctor" << std::endl; }
  Widget(Widget &&) { std::cerr << "move ctor" << std::endl; }
  Widget &operator=(Widget const &) { std::cerr << "copy assign" << std::endl; }
  Widget &operator=(Widget &&) { std::cerr << "move assign" << std::endl; }
  int i_;
};

auto foo(){
  std::pair<Widget,Widget> w;  // default construction
  auto &[w1,w2] = w;
  // do something with w1 and w2
  return w;
}

int main(){
  auto f = foo();
}

No copy is made but now I tried to use make_pair

auto foo(){
  auto w = std::make_pair<Widget,Widget>({},{}); // Awkward syntax and move construction
  auto &[w1,w2] = w;
  // do something with w1 and w2
  return w;
}

Is this really the only possible alternative if I want to use make_pair? Any why is there move construction involved as compared to the first function?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
CD86
  • 979
  • 10
  • 27
  • Why do you care so much about using `std::make_pair()` if what you want is default-constructed objects? –  Oct 10 '18 at 19:56
  • There is no standard `` header file – Fureeish Oct 10 '18 at 19:58
  • for reasons of consistency with regard to std::make_optional etc. – CD86 Oct 10 '18 at 20:14
  • 1
    If you value consistency, then relying on the optimizer getting rid of that overhead 99.9% of the time is generally enough. If you do care about that .1%, then compromises on consistency are ok for that purpose. –  Oct 10 '18 at 20:29

1 Answers1

3

I think the premise of your question is wrong.

Is this really the only possible alternative if I want to use make_pair?

Why would you use std::make_pair here?

auto w = std::make_pair<Widget,Widget>({},{}); // Awkward syntax and move construction

This really is awkward syntax. Let me explain why I think so...

From cppreference on std::make_pair (emphasize mine):

Creates a std::pair object, deducing the target type from the types of arguments.

The only purpose of std::make_pair is to deduce the types of its arguments, as for example in

std::pair<int,int> x;                         
x = std::pair<int,int>(3,3);     // not nice      
x = std::make_pair(3,3);         // ok

When default constructing a pair and you know its type, you never had to repeat the template parameters

std::pair<int,int> a = std::pair<int,int>();  // awkward 

Neither when you want non-default construction (just dont use auto when its only effect is that you have to spell the type elsewhere in the same line of code)

std::pair<int,int> a{5,4}; // no move/copy

Bottomline:

The alternative is to simply not use std::make_pair when you don't need it.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185