Qual é o propósito std::make_pair
?
Por que não apenas fazer std::pair<int, char>(0, 'a')
?
Existe alguma diferença entre os dois métodos?
std::make_pair
é redundante. Há uma resposta abaixo que detalha isso.
Qual é o propósito std::make_pair
?
Por que não apenas fazer std::pair<int, char>(0, 'a')
?
Existe alguma diferença entre os dois métodos?
std::make_pair
é redundante. Há uma resposta abaixo que detalha isso.
Respostas:
A diferença é que, com std::pair
você, é necessário especificar os tipos de ambos os elementos, ao passo std::make_pair
que criará um par com o tipo dos elementos que são passados para ele, sem a necessidade de informar. É o que eu pude reunir de vários documentos.
Veja este exemplo em http://www.cplusplus.com/reference/std/utility/make_pair/
pair <int,int> one;
pair <int,int> two;
one = make_pair (10,20);
two = make_pair (10.5,'A'); // ok: implicit conversion from pair<double,char>
Além do bônus implícito de conversão, se você não usasse make_pair, teria que fazer
one = pair<int,int>(10,20)
toda vez que você atribuiu a um, o que seria irritante ao longo do tempo ...
std::make_pair
. Aparentemente, é apenas por conveniência.
one = {10, 20}
hoje em dia, mas não tenho um compilador C ++ 11 à mão para verificá-lo.
make_pair
funciona com tipos sem nome, incluindo estruturas, uniões, lambdas e outros doodads.
Como o @MSalters respondeu acima, agora você pode usar chaves para fazer isso no C ++ 11 (verifique isso apenas com um compilador C ++ 11):
pair<int, int> p = {1, 2};
Argumentos de modelo de classe não puderam ser deduzidos do construtor antes do C ++ 17
Antes do C ++ 17, você não podia escrever algo como:
std::pair p(1, 'a');
pois isso inferiria tipos de modelo a partir dos argumentos do construtor.
O C ++ 17 torna essa sintaxe possível e, portanto, make_pair
redundante.
Antes do C ++ 17, std::make_pair
nos permitia escrever um código menos detalhado:
MyLongClassName1 o1;
MyLongClassName2 o2;
auto p = std::make_pair(o1, o2);
em vez do mais detalhado:
std::pair<MyLongClassName1,MyLongClassName2> p{o1, o2};
que repete os tipos e pode ser muito longo.
A inferência de tipo funciona nesse caso anterior ao C ++ 17 porque make_pair
não é um construtor.
make_pair
é essencialmente equivalente a:
template<class T1, class T2>
std::pair<T1, T2> my_make_pair(T1 t1, T2 t2) {
return std::pair<T1, T2>(t1, t2);
}
O mesmo conceito se aplica a inserter
vs insert_iterator
.
Veja também:
Exemplo mínimo
Para tornar as coisas mais concretas, podemos observar o problema minimamente com:
main.cpp
template <class MyType>
struct MyClass {
MyType i;
MyClass(MyType i) : i(i) {}
};
template<class MyType>
MyClass<MyType> make_my_class(MyType i) {
return MyClass<MyType>(i);
}
int main() {
MyClass<int> my_class(1);
}
então:
g++-8 -Wall -Wextra -Wpedantic -std=c++17 main.cpp
compila alegremente, mas:
g++-8 -Wall -Wextra -Wpedantic -std=c++14 main.cpp
falha com:
main.cpp: In function ‘int main()’:
main.cpp:13:13: error: missing template arguments before ‘my_class’
MyClass my_class(1);
^~~~~~~~
e requer, em vez disso, trabalhar:
MyClass<int> my_class(1);
ou o ajudante:
auto my_class = make_my_class(1);
que usa uma função regular em vez de um construtor.
Diferença para `std :: reference_wrapper
Este comentário menciona que std::make_pair
desembrulhastd::reference_wrapper
enquanto o construtor não, então essa é uma diferença. Exemplo TODO.
Testado com o GCC 8.1.0, Ubuntu 16.04 .
std::make_pair
não foi preterido no C ++ 17?
make_pair
desembrulha invólucros de referência, por isso é diferente do CTAD.
Não há diferença entre usar make_pair
e chamar explicitamente o pair
construtor com argumentos de tipo especificado. std::make_pair
é mais conveniente quando os tipos são detalhados porque um método de modelo possui dedução de tipo com base em seus parâmetros fornecidos. Por exemplo,
std::vector< std::pair< std::vector<int>, std::vector<int> > > vecOfPair;
std::vector<int> emptyV;
// shorter
vecOfPair.push_back(std::make_pair(emptyV, emptyV));
// longer
vecOfPair.push_back(std::pair< std::vector<int>, std::vector<int> >(emptyV, emptyV));
Vale a pena notar que esse é um idioma comum na programação de modelos C ++. É conhecido como o idioma do Gerador de Objetos, você pode encontrar mais informações e um bom exemplo aqui .
Editar Como alguém sugeriu nos comentários (desde que removido), a seguir é um extrato ligeiramente modificado do link, caso ele se quebre.
Um gerador de objetos permite a criação de objetos sem especificar explicitamente seus tipos. Ele se baseia em uma propriedade útil dos modelos de função que os modelos de classe não possuem: Os parâmetros de tipo de um modelo de função são deduzidos automaticamente de seus parâmetros reais. std::make_pair
é um exemplo simples que retorna uma instância do std::pair
modelo, dependendo dos parâmetros reais da std::make_pair
função.
template <class T, class U>
std::pair <T, U>
make_pair(T t, U u)
{
return std::pair <T, U> (t,u);
}
&&
desde C ++ 11.
make_pair cria uma cópia extra sobre o construtor direto. Eu sempre digitava meus pares para fornecer uma sintaxe simples.
Isso mostra a diferença (exemplo de Rampal Chaudhary):
class Sample
{
static int _noOfObjects;
int _objectNo;
public:
Sample() :
_objectNo( _noOfObjects++ )
{
std::cout<<"Inside default constructor of object "<<_objectNo<<std::endl;
}
Sample( const Sample& sample) :
_objectNo( _noOfObjects++ )
{
std::cout<<"Inside copy constructor of object "<<_objectNo<<std::endl;
}
~Sample()
{
std::cout<<"Destroying object "<<_objectNo<<std::endl;
}
};
int Sample::_noOfObjects = 0;
int main(int argc, char* argv[])
{
Sample sample;
std::map<int,Sample> map;
map.insert( std::make_pair( 1, sample) );
//map.insert( std::pair<int,Sample>( 1, sample) );
return 0;
}
std::move
apenas dentro insert
e / ou em torno do que seria uma referência sample
. Somente quando mudo std::map<int,Sample>
para std::map<int,Sample const&>
isso reduzo o número de objetos construídos e somente quando excluo o construtor de cópias é que elimino todas as cópias (obviamente). Depois de fazer essas duas alterações, meu resultado inclui uma chamada para o construtor padrão e duas chamadas para o destruidor para o mesmo objeto. Eu acho que devo estar faltando alguma coisa. (g ++ 5.4.1, c ++ 11)
emplace
vez de, insert
se você está construindo um valor para inserir imediatamente (e não deseja instâncias extras). Não é minha área de especialização, se posso dizer que tenho uma, mas copiar / mover a semântica introduzida pelo C ++ 11 me ajudou muito.
a partir do c ++ 11, basta usar a inicialização uniforme para pares. Então, em vez de:
std::make_pair(1, 2);
ou
std::pair<int, int>(1, 2);
Apenas use
{1, 2};
{1, 2}
pode ser usado para inicializar um par, mas não confirma para o par de tipos. Ou seja, ao usar auto você tem de se comprometer com um tipo no RHS: auto p = std::pair{"Tokyo"s, 9.00};
.