Jaki jest cel std::make_pair
?
Dlaczego po prostu nie zrobić std::pair<int, char>(0, 'a')
?
Czy jest jakaś różnica między tymi dwiema metodami?
std::make_pair
jest zbędny. Poniżej znajduje się odpowiedź, która szczegółowo to wyjaśnia.
Jaki jest cel std::make_pair
?
Dlaczego po prostu nie zrobić std::pair<int, char>(0, 'a')
?
Czy jest jakaś różnica między tymi dwiema metodami?
std::make_pair
jest zbędny. Poniżej znajduje się odpowiedź, która szczegółowo to wyjaśnia.
Odpowiedzi:
Różnica polega na tym, std::pair
że musisz określić typy obu elementów, podczas gdy std::make_pair
utworzysz parę z typem elementów, które są do niego przekazywane, bez konieczności mówienia. Tak czy inaczej mogłem zebrać z różnych dokumentów.
Zobacz ten przykład z 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>
Oprócz niejawnej premii za konwersję, jeśli nie używasz make_pair, musisz to zrobić
one = pair<int,int>(10,20)
za każdym razem, gdy przydzielasz do jednego, co byłoby denerwujące z czasem ...
std::make_pair
. Najwyraźniej to tylko dla wygody.
one = {10, 20}
dzisiejszych czasach, ale nie mam pod ręką kompilatora C ++ 11, aby to sprawdzić.
make_pair
działa z nienazwanymi typami, w tym strukturami, związkami, lambdami i innymi elementami dekoracyjnymi.
Jak @MSalters odpowiedział powyżej, możesz teraz użyć nawiasów klamrowych, aby to zrobić w C ++ 11 (właśnie zweryfikowano to za pomocą kompilatora C ++ 11):
pair<int, int> p = {1, 2};
Nie można wywnioskować argumentów szablonu klasy z konstruktora przed C ++ 17
Przed C ++ 17 nie można było napisać czegoś takiego:
std::pair p(1, 'a');
ponieważ mogłoby to wywnioskować typy szablonów z argumentów konstruktora.
C ++ 17 umożliwia taką składnię, a zatem jest make_pair
zbędna.
Przed C ++ 17 std::make_pair
pozwalało nam pisać mniej rozwlekły kod:
MyLongClassName1 o1;
MyLongClassName2 o2;
auto p = std::make_pair(o1, o2);
zamiast bardziej rozwlekłych:
std::pair<MyLongClassName1,MyLongClassName2> p{o1, o2};
który powtarza typy i może być bardzo długi.
Wnioskowanie o typie działa w tym przypadku sprzed wersji C ++ 17, ponieważ make_pair
nie jest konstruktorem.
make_pair
jest zasadniczo równoważne z:
template<class T1, class T2>
std::pair<T1, T2> my_make_pair(T1 t1, T2 t2) {
return std::pair<T1, T2>(t1, t2);
}
To samo odnosi się do koncepcji inserter
vs insert_iterator
.
Zobacz też:
Minimalny przykład
Aby uczynić rzeczy bardziej konkretnymi, możemy zaobserwować problem w minimalnym stopniu:
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);
}
następnie:
g++-8 -Wall -Wextra -Wpedantic -std=c++17 main.cpp
kompiluje się szczęśliwie, ale:
g++-8 -Wall -Wextra -Wpedantic -std=c++14 main.cpp
zawodzi z:
main.cpp: In function ‘int main()’:
main.cpp:13:13: error: missing template arguments before ‘my_class’
MyClass my_class(1);
^~~~~~~~
i zamiast tego wymaga pracy:
MyClass<int> my_class(1);
lub pomocnik:
auto my_class = make_my_class(1);
który używa zwykłej funkcji zamiast konstruktora.
Różnica dla `std :: reference_wrapper
Ten komentarz wspomina, że std::make_pair
rozpakowuje się, std::reference_wrapper
podczas gdy konstruktor tego nie robi, więc to jedna różnica. Przykład TODO.
Testowane z GCC 8.1.0, Ubuntu 16.04 .
std::make_pair
nie stało się przestarzałe w C ++ 17?
make_pair
rozpakowuje opakowania referencyjne, więc w rzeczywistości różni się od CTAD.
Nie ma różnicy między użyciem make_pair
a jawnym wywołaniem pair
konstruktora z określonymi argumentami typu. std::make_pair
jest wygodniejsze, gdy typy są rozwlekłe, ponieważ metoda szablonu ma dedukcję typu na podstawie podanych parametrów. Na przykład,
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));
Warto zauważyć, że jest to powszechny idiom w programowaniu szablonów w C ++. Jest znany jako idiom Object Generator, możesz znaleźć więcej informacji i ładny przykład tutaj .
Edycja Jak ktoś zasugerował w komentarzach (od czasu usunięcia), poniżej znajduje się nieznacznie zmodyfikowany wyciąg z łącza na wypadek, gdyby się zepsuł.
Generator obiektów umożliwia tworzenie obiektów bez jawnego określania ich typów. Opiera się on na użytecznej właściwości szablonów funkcji, których szablony klas nie mają: parametry typu szablonu funkcji są wyprowadzane automatycznie z jego rzeczywistych parametrów. std::make_pair
to prosty przykład, który zwraca wystąpienie std::pair
szablonu w zależności od rzeczywistych parametrów std::make_pair
funkcji.
template <class T, class U>
std::pair <T, U>
make_pair(T t, U u)
{
return std::pair <T, U> (t,u);
}
&&
od C ++ 11.
make_pair tworzy dodatkową kopię nad konstruktorem direct. Zawsze wpisuję moje pary, aby zapewnić prostą składnię.
To pokazuje różnicę (przykład autorstwa Rampala 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
tylko w środku insert
i / lub w pobliżu tego, do czego byłoby odniesienie sample
. Dopiero gdy zmieniam std::map<int,Sample>
na std::map<int,Sample const&>
to zmniejszam liczbę konstruowanych obiektów i dopiero kiedy usuwam konstruktor kopiujący, eliminuję wszystkie kopie (oczywiście). Po wprowadzeniu obu tych zmian mój wynik obejmuje jedno wywołanie domyślnego konstruktora i dwa wywołania destruktora dla tego samego obiektu. Myślę, że czegoś mi brakuje. (g ++ 5.4.1, c ++ 11)
emplace
zamiast insert
konstruować wartość do natychmiastowego wstawienia (i nie chcesz dodatkowych instancji). To nie jest moja specjalizacja, jeśli mogę nawet powiedzieć, że ją mam, ale kopiuj / przenieś semantyka wprowadzona przez C ++ 11 bardzo mi pomogła.
zaczynając od c ++ 11 po prostu użyj jednolitej inicjalizacji dla par. Więc zamiast:
std::make_pair(1, 2);
lub
std::pair<int, int>(1, 2);
po prostu użyj
{1, 2};
{1, 2}
może być użyty do zainicjowania pary, ale nie zatwierdza dla pary typów. Czyli przy użyciu auto trzeba zobowiązać się do określonego typu na RHS: auto p = std::pair{"Tokyo"s, 9.00};
.