2012-03-09 12 views
68

Estaba jugando con g ++ 4.7 (una de las instantáneas posteriores) con -std = C++ 11 activado. Traté de compilar parte de mi código base existente y un caso que falló algo me confunde.C++ 11 make_pair con parámetros de plantilla especificados no compila

Agradecería que alguien pudiera explicarme lo que está sucediendo.

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

entiendo que make_pair es significaba para ser utilizado como la (1) caso (si puedo especificar los tipos, entonces yo también podría utilizar (3)), pero No entiendo por qué está fallando en este caso.

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: /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&&’

Una vez más, la cuestión aquí es simplemente "¿qué está pasando?" Sé que puedo solucionar el problema eliminando la especificación de la plantilla, pero solo quiero saber qué está fallando aquí bajo las sábanas. Gracias por adelantado.

EDIT:

  • g ++ 4.4 compila el código sin problemas.
  • Removing -std = C++ 11 también compila con código sin problemas.
+5

Una pregunta excelente. Otro ejemplo de un cambio de ruptura sutil en C++ 11, similar a [el cambio de última hora en la construcción de 'std :: vector'] (http://stackoverflow.com/questions/5759232/stdvector-default-construction-c11- and-breaking-changes). Al menos, esto produce un error de compilación y no un cambio silencioso en la semántica. –

+1

Si tengo una variable entera i. Quiero hacer pareja con i y otro objeto. ¿Cómo debería llamar makepair exactamente? 1) make_pair <*i, obj> 2) int && j = i; make_pair ? Ambos no están funcionando. ¿Cuál es la forma correcta de hacerlo? – PHcoDer

Respuesta

108

No es así como std::make_pair está destinado a ser utilizado; no se supone que especifiques explícitamente los argumentos de la plantilla.

El C++ 11 std::make_pair toma dos argumentos, de tipo T&& y U&&, donde T y U son parámetros de tipo de plantilla. Efectivamente, parece que este (ignorando el tipo de retorno):

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

Cuando se llama a std::make_pair y especificar explícitamente los argumentos de tipo plantilla, ninguna deducción argumento lleva a cabo. En cambio, los argumentos de tipo son sustituidos directamente en la declaración de plantilla, obteniéndose:

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

Nota que estos dos tipos de parámetros son referencias rvalue. Por lo tanto, solo pueden unirse a valores r. Esto no es un problema para el segundo argumento que pasa, 7, porque esa es una expresión rvalue. s, sin embargo, es una expresión lvalue (no es temporal y no se está moviendo). Esto significa que la plantilla de función no coincide con sus argumentos, por lo que obtiene el error.

Entonces, ¿por qué funciona cuando no se especifica explícitamente qué T y U están en la lista de argumentos de la plantilla? En resumen, los parámetros de referencia rvalue son especiales en las plantillas. Debido en parte a una función de idioma llamada referencia colapsando, un parámetro de referencia rvalue de tipo A&&, donde A es un parámetro de tipo de plantilla, puede vincularse a cualquier tipo de A.

No importa si el A es un lvalue, un valor p, const-calificado, volátil-calificado o no calificado, un A&& puede unirse a ese objeto (de nuevo, si y sólo si A es en sí misma un parámetro de plantilla)

En su ejemplo, hacemos la llamada:

make_pair(s, 7) 

Aquí, s es un valor-I de tipo std::string y 7 es un valor p de tipo int. Como no especifica los argumentos de plantilla para la plantilla de función, la deducción de argumento de plantilla se realiza para descubrir cuáles son los argumentos.

Para enlazar s, un valor-I, a T&&, el compilador deduce T ser std::string&, produciendo un argumento de tipo std::string& &&. Sin embargo, no hay referencias a referencias, por lo que esta "referencia doble" colapsa para convertirse en std::string&. s es un partido.

Es simple para unirse 7 a U&&: el compilador puede deducir U ser int, produciendo un parámetro de tipo int&&, que se une con éxito para 7 porque es un valor p.

Hay un montón de sutilezas con estas nuevas características del lenguaje, pero si usted sigue una regla simple, es bastante fácil:

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.

+6

Esta es una explicación muy buena y completa. ¡Gracias! – vmpstr

+1

@James - ¿Es esa "regla simple" de otro artículo o respuesta que debería leer? –

+4

@MichaelBurr: Nah, acabo de inventarlo. :-) Entonces, ¡espero que sea verdad! Creo que es verdad ... esa regla funciona para mí casi todo el tiempo. –

Cuestiones relacionadas