I’m using std::make_pair() for this example because pretty much any C++ programmer ought to be familiar with it, but more generally I’m wondering about the pattern it uses.
It occurred to me that, although I enjoy the convenience of std::make_pair() it makes an “extra” copy of each argument, since it creates a pair and returns it by value. If I then use that to insert into an STL container that means that actually each parameter is copied a total of 3 times… I wrote this code snippet to illustrate (along with some attempts to improve it without losing too much convenience):
#include <iostream>
#include <utility>
#include <list>
using namespace std;
// C++11 only:
#define MAKE_PAIR(a,b) pair<decltype(a),decltype(b)>((a),(b))
class A {
public:
A () { }
A (const A& a) {
cout << "\tCopy constructor called" << endl;
}
};
int main()
{
list<pair<int,A> > l;
cout << "Using std::make_pair()" << endl;
l.push_back(make_pair(10,A()));
cout << "Using MAKE_PAIR()" << endl;
l.push_back(MAKE_PAIR(10,A()));
typedef pair<int, A> my_pair;
cout << "Using a typedef" << endl;
l.push_back(my_pair(10,A()));
}
which produces the output:
Using std::make_pair()
Copy constructor called
Copy constructor called
Copy constructor called
Using MAKE_PAIR()
Copy constructor called
Copy constructor called
Using a typedef
Copy constructor called
Copy constructor called
I realize there are a couple other copies here that can probably be eliminated (or rather reduced to pointer/smart pointer copies), for example by using A * or a smart pointer in the pair instead, and allocating it myself.
The macro idea (which requires C++11) seemed interesting to me, although I know many are not fond of macros. The typedef works fine too, but then you have to create a separate typedef for each set of template args, and so it’s more convenient than specifying the template args explicitly each time, it’s still not quite as nice as the wrapper function.
I’m wondering does anybody actually avoid make_pair() in practice for this reason? Does C++/C++11 offer any other interesting solutions?
I like this idea of creating a templated function wrapper around a constructor so that we can deduce the template arguments, but I’m not as crazy about causing a runtime impact because of it.
In my compiler, with and without optimizations, the extra copy constructor was optimized away.
If I add the parameter
-fno-elide-constructors, then we see the extra constructors.The C++ Spec has this to say on skipping copy constructors in [class.copy.15] of the 2003 Spec: