C ++ 11 make_pair con parámetros de plantilla especificados no se compila

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.

Aquí está el código:

#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 significó 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.

El error exacto es:

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:
note: template<class _T1, class _T2> constexpr std::pair<typename std::__decay_and_strip<_T1>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
note:   template argument deduction/substitution failed:
    test.cpp:11:83: note:   cannot convert ‘s’ (type ‘std::string {aka std::basic_string<char>}’) to type ‘std::basic_string<char>&&’

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.

  • g++ 4.4 compiles this code with no problems.
  • Removing -std=c++11 also compiles with code with no problems.

preguntado el 09 de marzo de 12 a las 22:03

An excellent question. Yet another example of a subtle breaking change in C++11, similar to the breaking change in std::vector construcción. At least this one yields a compiler error and not a silent change in semantics. -

If I have an integer variable i. I want to make pair with i and another object. How exactly should I call makepair. 1) make_pair<*i, obj> 2) int&& j = i; make_pair<j, obj>? Both are not working. Whats the correct way to do it? -

1 Respuestas

Asi no es como std::make_pair is intended to be used; you are not supposed to explicitly specify the template arguments.

El C ++ 11 std::make_pair takes two arguments, of type T&& y U&&, Donde T y U are template type parameters. Effectively, it looks like this (ignoring the return type):

template <typename T, typename U>
[return type] make_pair(T&& argT, U&& argU);

Cuando usted llama 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:

[return type] make_pair(std::string&& argT, int&& argU);

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 y U are in the template argument list? In short, rvalue reference parameters are special in templates. Due in part to a language feature called colapso de referencia, an rvalue reference parameter of type A&&, Donde A is a template type parameter, can bind to any kind of A.

No importa si el A is an lvalue, an rvalue, const-qualified, volatile-qualified, or unqualified, an A&& can bind to that object (again, if and only if A is itself a template parameter).

In your example, we make the call:

make_pair(s, 7)

Aquí, s es un valor de tipo std::string y 7 es un rvalue de tipo int. Since you do not specify the template arguments for the function template, template argument deduction is performed to figure out what the arguments are.

Para unir s, an lvalue, to T&&, the compiler deduces T para ser std::string&, yielding an argument of type std::string& &&. There are no references to references, though, so this "double reference" collapses to become std::string&. s es un partido.

It's simple to bind 7 a U&&: the compiler can deduce U para ser int, yielding a parameter of type int&&, which binds successfully to 7 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:

If a template argument can be deduced from the function arguments, let it be deduced. Don't explicitly provide the argument unless you absolutely must.

Let the compiler do the hard work, and 99.9% of the time it'll be exactly what you wanted anyway. When it isn't what you wanted, you'll usually get a compilation error which is easy to identify and fix.

Respondido 02 Feb 21, 21:02

This is a very good and comprehensive explanation. Thank you! - vmpstr

@James - is that "one simple rule" from another article or answer I should read? - miguel rebabas

@MichaelBurr: Nah, I just made that up. :-) So, I hope it's true! I think it's true... that rule works for me pretty much all the time. - james mcnellis

@James: thanks. The 'quote box' around it made me think it might have been something originally written elsewhere. This answer was really informative, and I just wanted to make sure I wasn't missing something somewhere else. - miguel rebabas

Does this apply to tuples as well? - Ferruccio

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.