¿Para qué sirve std::make_pair
?
¿Por qué no simplemente hacer std::pair<int, char>(0, 'a')
?
¿Hay alguna diferencia entre los dos métodos?
std::make_pair
es redundante. Hay una respuesta a continuación que detalla esto.
¿Para qué sirve std::make_pair
?
¿Por qué no simplemente hacer std::pair<int, char>(0, 'a')
?
¿Hay alguna diferencia entre los dos métodos?
std::make_pair
es redundante. Hay una respuesta a continuación que detalla esto.
Respuestas:
La diferencia es que std::pair
necesita especificar los tipos de ambos elementos, mientras std::make_pair
que creará un par con el tipo de elementos que se le pasan, sin necesidad de contarlo. Eso es lo que podría reunir de varios documentos de todos modos.
Vea este ejemplo de 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>
Aparte de la bonificación de conversión implícita, si no usaras make_pair, deberías hacer
one = pair<int,int>(10,20)
cada vez que asignas a uno, lo que sería molesto con el tiempo ...
std::make_pair
. Aparentemente es solo por conveniencia.
one = {10, 20}
hoy en día, pero no tengo un compilador de C ++ 11 a mano para comprobarlo.
make_pair
funciona con tipos sin nombre, incluidas estructuras, uniones, lambdas y otros objetos.
Como @MSalters respondió anteriormente, ahora puede usar llaves para hacer esto en C ++ 11 (acaba de verificar esto con un compilador de C ++ 11):
pair<int, int> p = {1, 2};
Los argumentos de la plantilla de clase no se pudieron inferir del constructor antes de C ++ 17
Antes de C ++ 17 no podías escribir algo como:
std::pair p(1, 'a');
ya que eso inferiría tipos de plantillas a partir de los argumentos del constructor.
C ++ 17 hace que esa sintaxis sea posible y, por lo tanto, make_pair
redundante.
Antes de C ++ 17, std::make_pair
nos permitía escribir código menos detallado:
MyLongClassName1 o1;
MyLongClassName2 o2;
auto p = std::make_pair(o1, o2);
en lugar del más detallado:
std::pair<MyLongClassName1,MyLongClassName2> p{o1, o2};
que repite los tipos y puede ser muy largo.
La inferencia de tipos funciona en ese caso anterior a C ++ 17 porque make_pair
no es un constructor.
make_pair
es esencialmente 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);
}
El mismo concepto se aplica a inserter
frente insert_iterator
.
Ver también:
Ejemplo mínimo
Para hacer las cosas más concretas, podemos observar el problema mínimamente con:
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);
}
luego:
g++-8 -Wall -Wextra -Wpedantic -std=c++17 main.cpp
compila felizmente, pero:
g++-8 -Wall -Wextra -Wpedantic -std=c++14 main.cpp
falla con:
main.cpp: In function ‘int main()’:
main.cpp:13:13: error: missing template arguments before ‘my_class’
MyClass my_class(1);
^~~~~~~~
y requiere en cambio trabajar:
MyClass<int> my_class(1);
o el ayudante:
auto my_class = make_my_class(1);
que usa una función regular en lugar de un constructor.
Diferencia para `std :: reference_wrapper
Este comentario menciona que se std::make_pair
desenvuelve std::reference_wrapper
mientras que el constructor no, así que esa es una diferencia. TODO ejemplo.
Probado con GCC 8.1.0, Ubuntu 16.04 .
std::make_pair
quedó obsoleto en C ++ 17?
make_pair
desenvuelve envoltorios de referencia, por lo que es diferente de CTAD en realidad.
No hay diferencia entre usar make_pair
y llamar explícitamente al pair
constructor con argumentos de tipo especificados. std::make_pair
es más conveniente cuando los tipos son detallados porque un método de plantilla tiene una deducción de tipos basada en sus parámetros dados. Por ejemplo,
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 la pena señalar que este es un idioma común en la programación de plantillas de C ++. Es conocido como el idioma del generador de objetos, puedes encontrar más información y un buen ejemplo aquí .
Editar Como alguien sugirió en los comentarios (desde que se eliminó), el siguiente es un extracto ligeramente modificado del enlace en caso de que se rompa.
Un generador de objetos permite la creación de objetos sin especificar explícitamente sus tipos. Se basa en una propiedad útil de las plantillas de función que las plantillas de clase no tienen: los parámetros de tipo de una plantilla de función se deducen automáticamente de sus parámetros reales. std::make_pair
es un ejemplo simple que devuelve una instancia de la std::pair
plantilla según los parámetros reales de la std::make_pair
función.
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 crea una copia adicional sobre el constructor directo. Siempre escribo dedef mis pares para proporcionar una sintaxis simple.
Esto muestra la diferencia (ejemplo 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
solo dentro insert
y / o alrededor de lo que sería una referencia sample
. Solo cuando cambio std::map<int,Sample>
a std::map<int,Sample const&>
eso reduzco el número de objetos construidos, y solo cuando elimino el constructor de copias elimino todas las copias (obviamente). Después de hacer ambos cambios, mi resultado incluye una llamada al constructor predeterminado y dos llamadas al destructor para el mismo objeto. Creo que me falta algo. (g ++ 5.4.1, c ++ 11)
emplace
lugar de insert
si solo está construyendo un valor para insertar de inmediato (y no desea instancias adicionales). No es mi área de especialización, incluso si puedo decir que tengo uno, sino copiar / mover La semántica introducida por C ++ 11 me ha ayudado mucho.
a partir de c ++ 11 solo use la inicialización uniforme para pares. Entonces en lugar de:
std::make_pair(1, 2);
o
std::pair<int, int>(1, 2);
Solo usa
{1, 2};
{1, 2}
se puede usar para inicializar un par, pero no se compromete para el tipo de par. Es decir, cuando el uso de auto que tiene que comprometerse a un tipo en el lado derecho: auto p = std::pair{"Tokyo"s, 9.00};
.