Apa tujuan std :: make_pair vs konstruktor std :: pair?


Jawaban:


165

Perbedaannya adalah bahwa dengan std::pairAnda perlu menentukan jenis kedua elemen, sedangkan std::make_pairakan membuat pasangan dengan jenis elemen yang diteruskan ke sana, tanpa Anda perlu mengatakannya. Itu yang bisa saya kumpulkan dari berbagai dokumen.

Lihat contoh ini dari 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>

Selain dari bonus konversi implisit, jika Anda tidak menggunakan make_pair, Anda harus melakukannya

one = pair<int,int>(10,20)

setiap kali Anda ditugaskan ke satu, yang akan mengganggu dari waktu ke waktu ...


1
Sebenarnya, jenis harus disimpulkan pada waktu kompilasi tanpa perlu menentukan.
Chad

@Tor Ya, saya tahu cara menggunakan keduanya, saya hanya ingin tahu apakah ada alasan untuk itu std::make_pair. Ternyata itu hanya untuk kenyamanan.

@ Jay Akan muncul begitu.
Tor Valamo

15
Saya pikir Anda dapat melakukannya one = {10, 20}saat ini tetapi saya tidak memiliki kompiler C ++ 11 yang berguna untuk memeriksanya.
MSalters

6
Perhatikan juga bahwa make_pairbekerja dengan jenis yang tidak disebutkan namanya, termasuk struct, serikat pekerja, lambdas, dan barang-barang kecil lainnya.
Mooing Duck

35

Seperti @MSalters menjawab di atas, Anda sekarang dapat menggunakan kurung kurawal untuk melakukan ini di C ++ 11 (cukup verifikasi ini dengan kompiler C ++ 11):

pair<int, int> p = {1, 2};

28

Argumen templat kelas tidak dapat disimpulkan dari konstruktor sebelum C ++ 17

Sebelum C ++ 17 Anda tidak dapat menulis sesuatu seperti:

std::pair p(1, 'a');

karena itu akan menyimpulkan tipe templat dari argumen konstruktor.

C ++ 17 membuat sintaks itu mungkin, dan karena itu make_pairberlebihan.

Sebelum C ++ 17, std::make_pairizinkan kami untuk menulis lebih sedikit kode verbose:

MyLongClassName1 o1;
MyLongClassName2 o2;
auto p = std::make_pair(o1, o2);

alih-alih yang lebih bertele-tele:

std::pair<MyLongClassName1,MyLongClassName2> p{o1, o2};

yang mengulang jenisnya, dan bisa sangat panjang.

Ketik inferensi berfungsi dalam kasus pra-C ++ 17 karena make_pairbukan merupakan konstruktor.

make_pair pada dasarnya setara dengan:

template<class T1, class T2>
std::pair<T1, T2> my_make_pair(T1 t1, T2 t2) {
    return std::pair<T1, T2>(t1, t2);
}

Konsep yang sama berlaku untuk insertervs insert_iterator.

Lihat juga:

Contoh minimal

Untuk membuat masalah lebih konkret, kita dapat mengamati masalah secara minimal dengan:

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);
}

kemudian:

g++-8 -Wall -Wextra -Wpedantic -std=c++17 main.cpp

mengkompilasi dengan senang hati, tetapi:

g++-8 -Wall -Wextra -Wpedantic -std=c++14 main.cpp

gagal dengan:

main.cpp: In function int main()’:
main.cpp:13:13: error: missing template arguments before my_class
     MyClass my_class(1);
             ^~~~~~~~

dan sebagai gantinya mengharuskan untuk bekerja:

MyClass<int> my_class(1);

atau pembantu:

auto my_class = make_my_class(1);

yang menggunakan fungsi biasa bukan konstruktor.

Perbedaan untuk `std :: reference_wrapper

Komentar ini menyebutkan bahwa std::make_pairmembukastd::reference_wrapper sementara konstruktor tidak, jadi itu satu perbedaan. Contoh TODO.

Diuji dengan GCC 8.1.0, Ubuntu 16.04 .


1
"C ++ 17 membuat sintaks itu mungkin, dan karenanya make_pair redundan." - Mengapa std::make_pairtidak usang di C ++ 17?
andreee

@ dan saya tidak yakin, alasan yang mungkin adalah tidak ada masalah sehingga tidak perlu memecahkan kode lama? Tapi saya tidak terbiasa dengan alasan komite C ++, ping saya jika Anda menemukan sesuatu.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
Satu hal berguna yang saya temui adalah kemampuan untuk menentukan tipe dengan std :: make_pair <T1, T2> (o1, o2) mencegah pengguna membuat kesalahan dengan meneruskan tipe o1 atau o2 yang tidak dapat secara implisit menjadi dilemparkan ke T1 atau T2. Misalnya melewati angka negatif ke int yang tidak ditandatangani. -Wsign-conversion -Werror tidak akan menangkap kesalahan ini dengan std :: pair constructor di c ++ 11 namun akan menangkap kesalahan jika std :: make_pair digunakan.
conchoecia

make_pairmembuka bungkus pembungkus referensi, jadi sebenarnya berbeda dari CTAD.
LF

26

Tidak ada perbedaan antara menggunakan make_pairdan secara eksplisit memanggil pairkonstruktor dengan argumen tipe tertentu. std::make_pairlebih nyaman ketika jenis verbose karena metode templat memiliki tipe deduksi berdasarkan parameter yang diberikan. Sebagai contoh,

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));

21

Perlu dicatat bahwa ini adalah idiom umum dalam pemrograman template C ++. Ini dikenal sebagai idiom Object Generator, Anda dapat menemukan informasi lebih lanjut dan contoh yang bagus di sini .

Edit Seperti seseorang menyarankan dalam komentar (sejak dihapus) berikut ini adalah ekstrak yang sedikit dimodifikasi dari tautan jika rusak.

Object Generator memungkinkan pembuatan objek tanpa secara spesifik menentukan jenisnya. Ini didasarkan pada properti berguna templat fungsi yang tidak dimiliki templat kelas: Parameter tipe templat fungsi disimpulkan secara otomatis dari parameter aktualnya. std::make_pairadalah contoh sederhana yang mengembalikan contoh std::pairtemplate tergantung pada parameter sebenarnya dari std::make_pairfungsi.

template <class T, class U>
std::pair <T, U> 
make_pair(T t, U u)
{
  return std::pair <T, U> (t,u);
}

2
@ sial Sebenarnya &&sejak C ++ 11.
Justme0

5

make_pair membuat salinan tambahan di atas konstruktor langsung. Saya selalu mengetikkan pasangan saya untuk memberikan sintaksis sederhana.
Ini menunjukkan perbedaan (contoh oleh 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;
}

4
Saya cukup yakin bahwa salinan tambahan akan hilang dalam semua kasus, jika pengaturan pengoptimalan dari kompiler cukup tinggi.
Björn Pollex

1
Mengapa Anda ingin mengandalkan optimisasi kompiler untuk kebenaran?
sjbx

Saya mendapatkan hasil yang sama dengan kedua versi, dan dengan std::movehanya di dalam insertdan / atau sekitar apa yang akan menjadi referensi sample. Hanya ketika saya mengubah std::map<int,Sample>ke std::map<int,Sample const&>saya mengurangi jumlah objek yang dibangun, dan hanya ketika saya menghapus copy constructor saya menghilangkan semua salinan (jelas). Setelah melakukan kedua perubahan tersebut, hasil saya mencakup satu panggilan ke konstruktor default dan dua panggilan ke destruktor untuk objek yang sama. Saya pikir saya pasti kehilangan sesuatu. (g ++ 5.4.1, c ++ 11)
John P

FWIW Saya setuju bahwa pengoptimalan dan kebenaran harus sepenuhnya independen, karena ini adalah jenis kode yang Anda tulis sebagai pemeriksaan kewarasan setelah tingkat pengoptimalan yang berbeda menghasilkan hasil yang tidak konsisten. Secara umum saya akan merekomendasikan emplacealih-alih insertjika Anda hanya membangun nilai untuk segera dimasukkan (dan Anda tidak ingin contoh tambahan.) Ini bukan bidang keahlian saya, jika saya dapat mengatakan saya memilikinya, tetapi salin / pindahkan semantik yang diperkenalkan oleh C ++ 11 telah banyak membantu saya.
John P

Saya percaya saya menemukan masalah yang persis sama dan setelah men-debug sekitar malam itu, saya akhirnya datang ke sini.
lllllllllllll

1

mulai dari c ++ 11 cukup gunakan inisialisasi seragam untuk pasangan. Jadi alih-alih:

std::make_pair(1, 2);

atau

std::pair<int, int>(1, 2);

gunakan saja

{1, 2};

{1, 2}dapat digunakan untuk menginisialisasi pasangan, tetapi tidak dilakukan untuk pasangan tipe. Yaitu ketika menggunakan auto Anda harus berkomitmen untuk tipe pada RHS: auto p = std::pair{"Tokyo"s, 9.00};.
Markus
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.