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?
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?
(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...
std::make_pair
. Apparently it is just for convenience. –
Shinn one = {10, 20}
nowadays but I don't have a C++11 compiler handy to check it. –
Expectorate make_pair
works with unnamed types, including structs, unions, lambdas, and other doodads. –
Straightaway 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.
std::make_pair
did not become deprecated in C++17? –
Spannew make_pair
unwraps reference wrappers, so it's different from CTAD actually. –
Galasyn 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};
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));
vecOfPair.emplace_back(emptyV, emptyV);
–
Mesonephros 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);
}
&&
since C++11. –
Conrad 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;
}
std::move
just inside insert
and/or around what would be a reference to sample
. It is only when I change std::map<int,Sample>
to std::map<int,Sample const&>
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) –
Conker 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. –
Conker 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, 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};
. –
Gnni I'm never going to use std::make_pair
again because it's "broken" (from my perspective - at least it's unintuitive). Consider the following example
You have a backing storage, or a string pool of
using Id = int;
std::vector<std::string> string_pool;
std::map<std::string_view, Id> str_to_id_map;
If you attempt to insert into std::map
like so;
str_to_id_map.insert(std::make_pair(string_pool.back(), 42));
This will fail, spectacularly. It will first create a copy of the std::string
which then will be turned into a std::string_view
- and now, that string_view is pointing to "anywhere". It might work, it might not work. I tested this just recently.
However, if you instead use
str_to_id_map.insert({string_pool.back(), 42});
This will correctly be deduced by the Compiler Gods as a string_view
over the string_pool contents and will work - no intermediary copy of a std::string
, turned into a string_view
My lesson or takeaway I gather from this is - never, ever, ever, ever use std::make_pair.
© 2022 - 2024 — McMap. All rights reserved.
std::make_pair
is redundant. There is an answer below that details this. – Balkin{ 0, 'a' }
(Anyone who's ever coded JavaScript for some time will especially love this.) – Simplexstd::make_pair(vec.cbegin(), vec.cend())
compared tostd::pair<std::vector<std::string>::const_iterator, std::vector<std::string>::const_iterator>(vec.cbegin(), vec.cend())
? – Straightaway