Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of std::make_pair vs the constructor of std::pair?

Tags:

c++

stl

std-pair

People also ask

Why do we use Make_pair in C++?

The make_pair() function, which comes under the Standard Template Library of C++, is mainly used to construct a pair object with two elements. In other words, it is a function that creates a value pair without writing the types explicitly.

What is std :: Make_pair?

std::make_pairConstructs a pair object with its first element set to x and its second element set to y . The template types can be implicitly deduced from the arguments passed to make_pair .

Is std :: pair a container?

std::pair is not a Container.


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


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:

  • Why not infer template parameter from constructor?
  • https://en.wikibooks.org/wiki/More_C++_Idioms/Object_Generator

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.


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));