What is the purpose of std::make_pair?

Why not just do std::pair<int, char>(0, 'a')?

Is there any difference between the two methods?

4

7 Answers

The difference is that with std::pair you need to specify the types of both elements, whereas std::make_pair will create a pair with the type of the elements that are passed to it, without you needing to tell it. That's what I could gather from various docs anyways.

See this example from

pair <int,int> one; pair <int,int> two; one = make_pair (10,20); two = make_pair (10.5,'A'); // ok: implicit conversion from pair<double,char> 

Aside from the implicit conversion bonus of it, if you didn't use make_pair you'd have to do

one = pair<int,int>(10,20) 

every time you assigned to one, which would be annoying over time...

4

As @MSalters replied above, you can now use curly braces to do this in C++11 (just verified this with a C++11 compiler):

pair<int, int> p = {1, 2}; 

Class template arguments could not be inferred from the constructor before C++17

Before C++17 you could not write something like:

std::pair p(1, 'a'); 

since that would infer template types from the constructor arguments, you had to write it explicitly as:

std::pair<int,char> p(1, 'a'); 

C++17 makes that syntax possible, and therefore make_pair redundant.

Before C++17, std::make_pair allowed us to write less verbose code:

MyLongClassName1 o1; MyLongClassName2 o2; auto p = std::make_pair(o1, o2); 

instead of the more verbose:

std::pair<MyLongClassName1,MyLongClassName2> p{o1, o2}; 

which repeats the types, and can be very long.

Type inference works in that pre-C++17 case because make_pair is not a constructor.

make_pair is essentially equivalent to:

template<class T1, class T2> std::pair<T1, T2> my_make_pair(T1 t1, T2 t2) { return std::pair<T1, T2>(t1, t2); } 

The same concept applies to inserter vs insert_iterator.

See also:

Minimal example

To make things more concrete, we can observe the problem minimally with:

main.cpp

template <class MyType> struct MyClass { MyType i; MyClass(MyType i) : i(i) {} }; template<class MyType> MyClass<MyType> make_my_class(MyType i) { return MyClass<MyType>(i); } int main() { MyClass<int> my_class(1); } 

then:

g++-8 -Wall -Wextra -Wpedantic -std=c++17 main.cpp 

compiles happily, but:

g++-8 -Wall -Wextra -Wpedantic -std=c++14 main.cpp 

fails with:

main.cpp: In function ‘int main()’: main.cpp:13:13: error: missing template arguments before ‘my_class’ MyClass my_class(1); ^~~~~~~~ 

and requires instead to work:

MyClass<int> my_class(1); 

or the helper:

auto my_class = make_my_class(1); 

which uses a regular function instead of a constructor.

Difference for std::reference_wrapper

This comment mentions that std::make_pair unwraps std::reference_wrapper while the constructor does not, so that's one difference. TODO example.

Tested with GCC 8.1.0, Ubuntu 16.04.

4

There is no difference between using make_pair and explicitly calling the pair constructor with specified type arguments. std::make_pair is more convenient when the types are verbose because a template method has type deduction based on its given parameters. For example,

std::vector< std::pair< std::vector<int>, std::vector<int> > > vecOfPair; std::vector<int> emptyV; // shorter vecOfPair.push_back(std::make_pair(emptyV, emptyV)); // longer vecOfPair.push_back(std::pair< std::vector<int>, std::vector<int> >(emptyV, emptyV)); 
1

It's worth noting that this is a common idiom in C++ template programming. It's known as the Object Generator idiom, you can find more information and a nice example here.

Edit As someone suggested in the comments (since removed) the following is a slightly modified extract from the link in case it breaks.

An Object Generator allows creation of objects without explicitly specifying their types. It is based on a useful property of function templates which class templates don't have: The type parameters of a function template are deduced automatically from its actual parameters. std::make_pair is a simple example that returns an instance of the std::pair template depending on the actual parameters of the std::make_pair function.

template <class T, class U> std::pair <T, U> make_pair(T t, U u) { return std::pair <T, U> (t,u); } 
1

make_pair creates an extra copy over the direct constructor. I always typedef my pairs to provide simple syntax.
This shows the difference (example by Rampal Chaudhary):

class Sample { static int _noOfObjects; int _objectNo; public: Sample() : _objectNo( _noOfObjects++ ) { std::cout<<"Inside default constructor of object "<<_objectNo<<std::endl; } Sample( const Sample& sample) : _objectNo( _noOfObjects++ ) { std::cout<<"Inside copy constructor of object "<<_objectNo<<std::endl; } ~Sample() { std::cout<<"Destroying object "<<_objectNo<<std::endl; } }; int Sample::_noOfObjects = 0; int main(int argc, char* argv[]) { Sample sample; std::map<int,Sample> map; map.insert( std::make_pair( 1, sample) ); //map.insert( std::pair<int,Sample>( 1, sample) ); return 0; } 
5

starting from c++11 just use uniform initialization for pairs. So instead of:

std::make_pair(1, 2); 

or

std::pair<int, int>(1, 2); 

just use

{1, 2}; 
1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy