I was just playing around with g++ 4.7 (one of the later snapshots) with -std=c++11 enabled. I tried to compile some of my existing code base and one case that failed somewhat confuses me.
I would appreciate if someone can explain what is going on.
Here's the code
#include <utility>
#include <iostream>
#include <vector>
#include <string>
int main ( )
{
std::string s = "abc";
// 1 ok
std::pair < std::string, int > a = std::make_pair ( s, 7 );
// 2 error on the next line
std::pair < std::string, int > b = std::make_pair < std::string, int > ( s, 7 );
// 3 ok
std::pair < std::string, int > d = std::pair < std::string, int > ( s, 7 );
return 0;
}
I understand that make_pair is meant to be used as the (1) case (if I specify the types, then I might as well use (3)), But I don't understand why it's failing in this case.
The exact error is:
test.cpp: In function ‘int main()’: test.cpp:11:83: error: no matching function for call to ‘make_pair(std::string&, int)’ test.cpp:11:83: note: candidate is: In file included from /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/utility:72:0, from test.cpp:1: /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5: note: template constexpr std::pair::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&) /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5: note: template argument deduction/substitution failed: test.cpp:11:83: note: cannot convert ‘s’ (type ‘std::string {aka std::basic_string}’) to type ‘std::basic_string&&’
Again, the question here is just "what's going on?" I know that I can fix the problem by removing the template specification, but I just want to know what's failing here under the covers. Thanks in advance.
EDIT:
- g++ 4.4 compiles this code with no problems.
- Removing -std=c++11 also compiles with code with no problems.
This is not how
std::make_pair
is intended to be used; you are not supposed to explicitly specify the template arguments.The C++11
std::make_pair
takes two arguments, of typeT&&
andU&&
, whereT
andU
are template type parameters. Effectively, it looks like this (ignoring the return type):When you call
std::make_pair
and explicitly specify the template type arguments, no argument deduction takes place. Instead, the type arguments are substituted directly into the template declaration, yielding:Note that both of these parameter types are rvalue references. Thus, they can only bind to rvalues. This isn't a problem for the second argument that you pass,
7
, because that is an rvalue expression.s
, however, is an lvalue expression (it isn't a temporary and it isn't being moved). This means the function template is not a match for your arguments, which is why you get the error.So, why does it work when you don't explicitly specify what
T
andU
are in the template argument list? In short, rvalue reference parameters are special in templates. Due in part to a language feature called reference collapsing, an rvalue reference parameter of typeA&&
, whereA
is a template type parameter, can bind to any kind ofA
.It doesn't matter whether the
A
is an lvalue, an rvalue, const-qualified, volatile-qualified, or unqualified, anA&&
can bind to that object (again, if and only ifA
is itself a template parameter).In your example, we make the call:
Here,
s
is an lvalue of typestd::string
and7
is an rvalue of typeint
. Since you do not specify the template arguments for the function template, template argument deduction is performed to figure out what the arguments are.To bind
s
, an lvalue, toT&&
, the compiler deducesT
to bestd::string&
, yielding an argument of typestd::string& &&
. There are no references to references, though, so this "double reference" collapses to becomestd::string&
.s
is a match.It's simple to bind
7
toU&&
: the compiler can deduceU
to beint
, yielding a parameter of typeint&&
, which binds successfully to7
because it is an rvalue.There are lots of subtleties with these new language features, but if you follow one simple rule, it's pretty easy: