238

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?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • 7
    In C++11, you can almost entirely do without make_pair. See [my answer](http://stackoverflow.com/a/21995963/2288585). – Debajit Mar 19 '14 at 06:54
  • 6
    In C++17, `std::make_pair` is redundant. There is an answer below that details this. – Drew Dormann May 22 '18 at 14:56
  • 1
    **TL;DR:** Just use curly braces. ;) `{ 0, 'a' }` (Anyone who's ever coded JavaScript for some time will especially love this.) – Andrew Sep 11 '20 at 04:14
  • 2
    `std::make_pair(vec.cbegin(), vec.cend())` compared to `std::pair::const_iterator, std::vector::const_iterator>(vec.cbegin(), vec.cend())`? – Mooing Duck Nov 11 '20 at 18:52

7 Answers7

210

(This answer is only correct for C++14 and earlier standards, due to CTAD)

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 http://www.cplusplus.com/reference/std/utility/make_pair/

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...

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
Tor Valamo
  • 33,261
  • 11
  • 73
  • 81
  • 2
    Actually, the types should be deduced at compile time without the need to specify. – Chad Feb 14 '12 at 01:51
  • @Tor Yeah, I know how to use both of them, I was just curious if there was a reason for `std::make_pair`. Apparently it is just for convenience. –  Feb 14 '12 at 01:56
  • 25
    I think you can do `one = {10, 20}` nowadays but I don't have a C++11 compiler handy to check it. – MSalters Feb 14 '12 at 08:11
  • 7
    Also note that `make_pair` works with unnamed types, including structs, unions, lambdas, and other doodads. – Mooing Duck Feb 06 '15 at 22:29
  • _"The difference is ... you need to specify the types of both elements"_ This answer is only correct for C++14 and earlier versions. – Drew Dormann Mar 27 '23 at 14:05
55

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.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • 3
    _"C++17 makes that syntax possible, and therefore make_pair redundant."_ - Why is it that `std::make_pair` did not become deprecated in C++17? – andreee Nov 06 '18 at 10:23
  • 3
    @andreee I'm not sure, possible reason is that it creates no trouble so no need to break old code? But I'm not familiar with C++ committee rationale, ping me if you find something. – Ciro Santilli OurBigBook.com Nov 06 '18 at 10:51
  • 3
    One useful thing that I have come across is that being able to specify the types with std::make_pair(o1, o2) prevents the user from making the mistake of passing types o1 or o2 that can't implicitly be cast to T1 or T2. For instance passing a negative number to an unsigned int. -Wsign-conversion -Werror will not catch this error with std::pair constructor in c++11 however it will catch the error if std::make_pair is used. – conchoecia Jan 30 '19 at 21:36
  • 1
    `make_pair` unwraps reference wrappers, so it's different from CTAD actually. – L. F. Mar 26 '20 at 04:48
51

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};
Martin
  • 12,408
  • 6
  • 34
  • 30
Debajit
  • 46,327
  • 33
  • 91
  • 100
26

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));
devil
  • 1,829
  • 16
  • 23
21

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);
}
mkm
  • 673
  • 5
  • 21
5

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;
}
EmpZoooli
  • 51
  • 1
  • 2
  • 4
    I am pretty sure that the extra copy will be elided in all cases, if the optimization settings of the compiler are high enough. – Björn Pollex Feb 19 '14 at 13:00
  • 1
    Why would you ever want to rely on compiler optimizations for correctness? – sjbx Dec 02 '16 at 08:46
  • I get the same results with both versions, and with `std::move` just inside `insert` and/or around what would be a reference to `sample`. It is only when I change `std::map` to `std::map` that I reduce the number of constructed objects, and only when I delete the copy constructor that I eliminate all copies (obviously). After making both of those changes, my result includes one call to the default constructor and two calls to the destructor for the same object. I think I must be missing something. (g++ 5.4.1, c++11) – John P Aug 30 '17 at 23:13
  • FWIW I agree that optimization and correctness should be completely independent, as this is exactly the kind of code you write as a sanity check after different optimization levels produce inconsistent results. In general I would recommend `emplace` instead of `insert` if you're just constructing a value to insert immediately (and you don't want extra instances.) It's not my area of expertise, if I can even say I have one, but the copy/move semantics introduced by C++11 have helped me a lot. – John P Aug 30 '17 at 23:31
  • I believe I am encountering exactly the same issue and after debugging for about the entire evening, I finally came here. – lllllllllllll Jun 24 '20 at 16:00
2

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};
Mahmoud Badri
  • 1,256
  • 14
  • 24
  • 3
    `{1, 2}` can be used to initialize a pair, but does not commit for type pair. I.e. when using auto you have to commit to a type on the RHS: `auto p = std::pair{"Tokyo"s, 9.00};`. – Markus Feb 28 '20 at 10:44