24

What on earth is going on here?
I'm trying to create a pair of an int and a string and I can create the pair if I use "magic values" but can't seem to pass in variables.

std::vector<std::pair<int, std::string> > num_text;

std::string text = "Smeg";
int num = 42;

// Works fine
num_text.push_back(std::make_pair<int, std::string>(42, std::string("Smeg")));  

// Cannot convert parameter 2 from 'std::string' to 'std::string &&'
num_text.push_back(std::make_pair<int, std::string>(42, text));

// Cannot convert parameter 1 from 'int' to 'int &&'
num_text.push_back(std::make_pair<int, std::string>(num, std::string("Smeg")));

// Cannot convert parameter 1 from 'int' to 'int &&'
num_text.push_back(std::make_pair<int, std::string>(num, text));

// Works fine again
num_text.push_back(std::make_pair<int, std::string>(42, std::string("Smeg")));

I am using VS 2012 and have pasted in some code that was written in VS 2008. Can't imagine that would have anything to do with it but there was no problem in the original (2008) code.

I kind of feel a bit dumb for not being able to workout what is going on here but what can I say, I just don't get it.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
user1277997
  • 285
  • 1
  • 2
  • 6

4 Answers4

26

Reference says:

template< class T1, class T2 >
std::pair<T1,T2> make_pair( T1 t, T2 u );           (until C++11)

template< class T1, class T2 >
std::pair<V1,V2> make_pair( T1&& t, T2&& u );       (since C++11)

Note that the return type is different. It also says:

The deduced types V1 and V2 are std::decay::type and std::decay::type (the usual type transformations applied to arguments of functions passed by value) unless application of std::decay results in std::reference_wrapper for some type X, in which case the deduced type is X&.

So in fact, since 2008 (I mean Visual C++ 2008), the semantics of the function make_pair has changed. You could either remove the template parameters from std::make_pair and let it deduce the type, or use std::pair's constructor if you need to make pairs of specific type:

num_text.push_back(std::make_pair(num, text));               // deduced type
num_text.push_back(std::pair<int, std::string>(num, text));  // specific type

The reason for the compile error is that you have specified the types to be int (as T1) and std::string (as T2), and therefore the function expects T1 && and T2 &&. See this answer for why that's a problem.

Community
  • 1
  • 1
Shahbaz
  • 46,337
  • 19
  • 116
  • 182
  • Or pass copy constructed temporaries. For example `num_text.push_back(std::make_pair(42, std::string(text)))` Or omit the template parameters. – Nikos C. Oct 14 '13 at 10:42
  • @NikosC., omit the parameters, of course, but your first solution not. I wouldn't send `42` instead of `num`! – Shahbaz Oct 14 '13 at 10:46
  • 1
    As I said, copy constructed temporaries. For passing `num`, you could use `num_text.push_back(std::make_pair(int(num), std::string(text)))`. – Nikos C. Oct 14 '13 at 10:53
  • What happened in 2008? By the way, explicitly specifying the template arguments will not work anyways, since the library implementation is free to pass through another function, and usual deduction rules will prevail again. For specifics, see [Stephan Lavavej's "Don't Help The Compiler" talk](http://channel9.msdn.com/Events/GoingNative/2013/Don-t-Help-the-Compiler) - it mentions this specific scenario – sehe Oct 16 '13 at 18:16
  • @sehe, nothing. The OP was asking that the code worked in VC++ 2008, so I was saying "since then ..." aka "since 2008 ...". By the way, I don't think I mentioned that the template arguments should be provided. Thanks for the link though. – Shahbaz Oct 17 '13 at 08:39
  • @sehe, are you talking about the code right above _"or simply..."_? It's using `std::pair`. But you are right. I would clarify what's the difference between the two. – Shahbaz Oct 17 '13 at 08:48
  • @sehe, can't figure out what caused a -1 though. Do you something wrong in there? – Shahbaz Oct 17 '13 at 12:31
  • @sehe Nice! How did you know/get that?! – Shahbaz Oct 17 '13 at 15:02
  • Let's move the comment discussion to the chat http://chat.stackoverflow.com/rooms/39431/discussion-between-shahbaz-and-sehe – sehe Oct 17 '13 at 15:15
17

make_pair<T1,T2> does not make a pair of type pair<T1,T2>, but rather deduces a suitable pair of reference types from its arguments to allow perfect forwarding. It's specified as

template <class T1, class T2>
pair<V1, V2> make_pair(T1&& x, T2&& y);

for some suitable reference types V1 and V2. This only works if the argument types are deduced, so that && can decay to an lvalue reference if necessary. By specifying the template parameters explicitly, they are no longer deduced, and so the function arguments can only be rvalues.

The solution is to let the compiler deduce the types:

num_text.push_back(std::make_pair(42, std::string("Smeg")));  // Works fine
num_text.push_back(std::make_pair(42, text));                 // Works fine
num_text.push_back(std::make_pair(num, std::string("Smeg"))); // Works fine
num_text.push_back(std::make_pair(num, text));                // Works fine
num_text.push_back(std::make_pair(42, std::string("Smeg")));  // Works fine again

If you need to make a pair of a particular type, don't use make_pair, just make a pair

// Works, but perhaps with more copying than you want.
num_text.push_back(std::pair<int, std::string>(num, text));   
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
6

make_pair is usually used without specifying the template parameters explicitly. This is how it is meant to be used:

num_text.push_back(std::make_pair(42, std::string("Smeg")));
num_text.push_back(std::make_pair(42, text));
num_text.push_back(std::make_pair(num, std::string("Smeg")));
num_text.push_back(std::make_pair(num, text));
num_text.push_back(std::make_pair(42, std::string("Smeg")));

Alternatively, if you want the exact type:

typedef decltype(num_text)::value_type value_type;
num_text.push_back(value_type(42, std::string("Smeg")));
num_text.push_back(value_type(42, text));
num_text.push_back(value_type(num, std::string("Smeg")));
num_text.push_back(value_type(num, text));
num_text.push_back(value_type(42, std::string("Smeg")));
Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
2

Now std::make_pair is defined the following way in the C++ Standard

template <class T1, class T2>

see below make_pair(**T1&&, T2&&**);

You could write simpler without using std::make_pair

num_text.push_back( { 42, text } ); .

Varaquilex
  • 3,447
  • 7
  • 40
  • 60
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335