Solo estaba jugando con g ++ 4.7 (una de las instantáneas posteriores) con -std = c ++ 11 habilitado. Traté de compilar algo de mi base de código existente y un caso que falló me confunde un poco.
Agradecería que alguien me pudiera explicar lo que está pasando.
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 está destinado a usarse como el caso (1) (si especifico los tipos, entonces también podría usar (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<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&&)
/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<char>}’) to type ‘std::basic_string<char>&&’
Una vez más, la pregunta 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í debajo de las cubiertas.
- g ++ 4.4 compila este código sin problemas.
- La eliminación de -std = c ++ 11 también se compila con código sin problemas.
std::vector
construcción . Al menos este produce un error de compilación y no un cambio silencioso en la semántica.Respuestas:
Esta no es la forma en
std::make_pair
que debe usarse; no se supone que debe especificar explícitamente los argumentos de la plantilla.C ++ 11
std::make_pair
toma dos argumentos, de tipoT&&
yU&&
, dondeT
yU
son parámetros de tipo de plantilla. Efectivamente, se ve así (ignorando el tipo de retorno):template <typename T, typename U> [return type] make_pair(T&& argT, U&& argU);
Cuando llama
std::make_pair
y especifica explícitamente los argumentos del tipo de plantilla, no se realiza ninguna deducción de argumentos. En cambio, los argumentos de tipo se sustituyen directamente en la declaración de plantilla, lo que produce:[return type] make_pair(std::string&& argT, int&& argU);
Tenga en cuenta que ambos tipos de parámetros son referencias de rvalue. Por lo tanto, solo se pueden vincular a rvalues. Esto no es un problema para el segundo argumento que pasa
7
, porque 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 la función no coincide con sus argumentos, razón por la cual obtiene el error.Entonces, ¿por qué funciona cuando no especifica explícitamente qué
T
y quéU
están en la lista de argumentos de la plantilla? En resumen, los parámetros de referencia de rvalue son especiales en las plantillas. Debido en parte a una característica del lenguaje llamada colapso de referencias , un parámetro de referencia rvalue de tipoA&&
, dondeA
es un parámetro de tipo de plantilla, se puede vincular a cualquier tipo deA
.No importa si
A
es un lvalue, un rvalue, const-calificado, volatile-calificado o no calificado, unA&&
puede vincularse a ese objeto (nuevamente, si y solo siA
es en sí mismo un parámetro de plantilla).En su ejemplo, hacemos la llamada:
make_pair(s, 7)
Aquí,
s
es un valor l de tipostd::string
y7
es un valor r de tipoint
. Dado que no especifica los argumentos de la plantilla para la plantilla de la función, la deducción de los argumentos de la plantilla se realiza para averiguar cuáles son los argumentos.Para enlazar
s
, un lvalue, toT&&
, el compilador deduceT
que esstd::string&
, produciendo un argumento de tipostd::string& &&
. Sin embargo, no hay referencias a referencias, por lo que esta "doble referencia" se colapsa para convertirse enstd::string&
.s
es un partido.Es sencillo al unen
7
aU&&
: el compilador puede deducirU
queint
, produciendo un parámetro de tipoint&&
, que se une al éxito7
porque es un valor de lado derecho.Hay muchas sutilezas con estas nuevas funciones de lenguaje, pero si sigue una regla simple, es bastante fácil:
fuente