Eu estava apenas brincando com o g ++ 4.7 (um dos instantâneos posteriores) com -std = c ++ 11 habilitado. Tentei compilar parte da minha base de código existente e um caso que falhou me confunde um pouco.
Eu apreciaria se alguém pudesse explicar o que está acontecendo.
Aqui está o 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;
}
Eu entendo que make_pair deve ser usado como o caso (1) (se eu especificar os tipos, então eu também posso usar (3)), mas não entendo por que ele está falhando neste caso.
O erro exato é:
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>&&’
Novamente, a questão aqui é apenas "o que está acontecendo?" Sei que posso corrigir o problema removendo a especificação do modelo, mas só quero saber o que está falhando aqui nos bastidores.
- g ++ 4.4 compila este código sem problemas.
- A remoção de -std = c ++ 11 também compila com o código sem problemas.
std::vector
construção . Pelo menos este produz um erro do compilador e não uma mudança silenciosa na semântica.Respostas:
Não é assim que
std::make_pair
se destina a ser usado; você não deve especificar explicitamente os argumentos do modelo.O C ++ 11
std::make_pair
leva dois argumentos, do tipoT&&
eU&&
, ondeT
eU
são parâmetros do tipo de modelo. Efetivamente, fica assim (ignorando o tipo de retorno):template <typename T, typename U> [return type] make_pair(T&& argT, U&& argU);
Quando você chama
std::make_pair
e especifica explicitamente os argumentos do tipo de modelo, nenhuma dedução de argumento ocorre. Em vez disso, os argumentos de tipo são substituídos diretamente na declaração do modelo, resultando em:[return type] make_pair(std::string&& argT, int&& argU);
Observe que ambos os tipos de parâmetro são referências rvalue. Portanto, eles só podem ser vinculados a rvalues. Isso não é um problema para o segundo argumento que você passa
7
,, porque essa é uma expressão rvalue.s
, no entanto, é uma expressão lvalue (não é temporária e não está sendo movida). Isso significa que o modelo de função não é compatível com seus argumentos, e é por isso que você obtém o erro.Então, por que funciona quando você não especifica explicitamente o que
T
e o queU
estão na lista de argumentos do modelo? Resumindo, os parâmetros de referência rvalue são especiais nos modelos. Devido em parte a um recurso de linguagem denominado colapso de referência , um parâmetro de referência rvalue do tipoA&&
, ondeA
é um parâmetro de tipo de modelo, pode ser vinculado a qualquer tipo deA
.Não importa se
A
é um lvalue, um rvalue, const-qualificado, volatile-qualificado ou não-qualificado,A&&
pode vincular-se a esse objeto (novamente, se e somente seA
for um parâmetro de modelo).No seu exemplo, fazemos a chamada:
make_pair(s, 7)
Aqui,
s
é um lvalue do tipostd::string
e7
é um rvalue do tipoint
. Visto que você não especifica os argumentos do template para o template da função, a dedução do argumento do template é realizada para descobrir quais são os argumentos.To bind
s
, an lvalue, toT&&
, o compilador deduzT
serstd::string&
, produzindo um argumento do tipostd::string& &&
. Não há referências a referências, entretanto, esta "referência dupla" se reduz para se tornarstd::string&
.s
é uma correspondência.É simples para ligar
7
paraU&&
: o compilador pode deduzirU
-seint
, produzindo um parâmetro do tipoint&&
, que se liga com sucesso7
, porque é um rvalue.Existem muitas sutilezas com esses novos recursos de linguagem, mas se você seguir uma regra simples, é muito fácil:
fonte