Mục đích của std :: make_ Pair so với hàm tạo của std :: cặp là gì?


180

Mục đích của là std::make_pairgì?

Tại sao không làm std::pair<int, char>(0, 'a')?

Có sự khác biệt nào giữa hai phương pháp không?


6
Trong C ++ 11, bạn gần như hoàn toàn có thể làm mà không cần make_ Pair. Xem câu trả lời của tôi .
Bệnh dịch hạch

2
Trong C ++ 17, std::make_pairlà dự phòng. Có một câu trả lời dưới đây chi tiết này.
Drew Dormann

Câu trả lời:


165

Sự khác biệt là với std::pairbạn cần chỉ định các loại của cả hai phần tử, trong khi đó std::make_pairsẽ tạo một cặp với loại phần tử được truyền cho nó, mà bạn không cần phải nói với nó. Đó là những gì tôi có thể thu thập từ nhiều tài liệu khác nhau.

Xem ví dụ này từ 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>

Ngoài phần thưởng chuyển đổi ngầm định của nó, nếu bạn không sử dụng make_ Pair bạn phải làm

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

mỗi lần bạn được chỉ định cho một người, điều đó sẽ gây phiền nhiễu theo thời gian ...


1
Trên thực tế, các loại nên được suy luận tại thời gian biên dịch mà không cần phải chỉ định.
Chad

@Tor Vâng, tôi biết cách sử dụng cả hai, tôi chỉ tò mò nếu có lý do cho std::make_pair. Rõ ràng nó chỉ để thuận tiện.

@Jay Nó sẽ xuất hiện như vậy.
Tor Valamo

15
Tôi nghĩ bạn có thể làm ngay one = {10, 20}hôm nay nhưng tôi không có trình biên dịch C ++ 11 để kiểm tra.
MSalters

6
Cũng lưu ý rằng make_pairhoạt động với các loại chưa được đặt tên, bao gồm các cấu trúc, đoàn thể, lambdas và hình tượng trưng khác.
Mooing Duck

35

Như @MSalters đã trả lời ở trên, bây giờ bạn có thể sử dụng dấu ngoặc nhọn để thực hiện điều này trong C ++ 11 (chỉ cần xác minh điều này với trình biên dịch C ++ 11):

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

28

Các đối số khuôn mẫu lớp không thể được suy ra từ hàm tạo trước C ++ 17

Trước C ++ 17, bạn không thể viết một cái gì đó như:

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

vì điều đó sẽ suy ra các kiểu mẫu từ các đối số của hàm tạo.

C ++ 17 làm cho cú pháp đó có thể, và do đó make_pairdư thừa.

Trước C ++ 17, std::make_paircho phép chúng tôi viết mã dài ít hơn:

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

thay vì dài dòng hơn:

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

mà lặp đi lặp lại các loại, và có thể rất dài.

Kiểu suy luận hoạt động trong trường hợp tiền C ++ 17 đó vì make_pairkhông phải là hàm tạo.

make_pair về cơ bản là tương đương với:

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

Khái niệm tương tự áp dụng cho insertervs insert_iterator.

Xem thêm:

Ví dụ tối thiểu

Để làm cho mọi thứ cụ thể hơn, chúng ta có thể quan sát vấn đề tối thiểu với:

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

sau đó:

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

vui vẻ biên dịch, nhưng:

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

thất bại với:

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

và yêu cầu thay vì làm việc:

MyClass<int> my_class(1);

hoặc người trợ giúp:

auto my_class = make_my_class(1);

trong đó sử dụng một hàm thông thường thay vì một hàm tạo.

Sự khác biệt cho `std :: Reference_wrapper

Nhận xét này đề cập đến việc std::make_pairhủy bỏ std::reference_wrappertrong khi nhà xây dựng thì không, vì vậy đó là một điểm khác biệt. Ví dụ TODO.

Đã thử nghiệm với GCC 8.1.0, Ubuntu 16.04 .


1
"C ++ 17 làm cho cú pháp đó có thể, và do đó làm cho dự phòng trở nên dư thừa." - Tại sao nó std::make_pairkhông bị phản đối trong C ++ 17?
andreee

@andreee Tôi không chắc, lý do có thể là nó không tạo ra rắc rối nên không cần phải phá mã cũ? Nhưng tôi không quen với lý do ủy ban C ++, hãy ping tôi nếu bạn tìm thấy thứ gì đó.
Ciro Santilli 郝海东 冠状 病 事件

1
Một điều hữu ích mà tôi đã gặp là việc có thể chỉ định các loại với std :: make_ Pair <T1, T2> (o1, o2) ngăn người dùng mắc lỗi chuyển các loại o1 hoặc o2 mà không thể ngầm hiểu truyền tới T1 hoặc T2. Ví dụ, truyền một số âm cho một số nguyên không dấu. -Wsign-convert -Werror sẽ không bắt lỗi này với std :: cặp constructor trong c ++ 11 tuy nhiên nó sẽ bắt lỗi nếu sử dụng std :: make_ Pair.
conchoecia

make_pairhủy đóng gói tham chiếu, vì vậy nó thực sự khác với CTAD.
LF

26

Không có sự khác biệt giữa việc sử dụng make_pairvà gọi rõ ràng hàm pairtạo với các đối số kiểu đã chỉ định. std::make_pairsẽ thuận tiện hơn khi các kiểu dài dòng vì một phương thức mẫu có loại trừ dựa trên các tham số đã cho. Ví dụ,

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

Điều đáng chú ý là đây là một thành ngữ phổ biến trong lập trình mẫu C ++. Nó được gọi là thành ngữ Object Generator, bạn có thể tìm thêm thông tin và một ví dụ hay ở đây .

Chỉnh sửa Như ai đó đã đề xuất trong các nhận xét (kể từ khi xóa), phần sau đây là một trích xuất được sửa đổi một chút từ liên kết trong trường hợp nó bị hỏng.

Trình tạo đối tượng cho phép tạo các đối tượng mà không chỉ định rõ ràng các loại của chúng. Nó dựa trên một thuộc tính hữu ích của các mẫu hàm mà các mẫu lớp không có: Các tham số loại của mẫu hàm được tự động suy ra từ các tham số thực tế của nó. std::make_pairlà một ví dụ đơn giản trả về một thể hiện của std::pairmẫu tùy thuộc vào các tham số thực tế của std::make_pairhàm.

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

2
@duck Thật ra &&từ C ++ 11.
Justme0

5

make_ Pair tạo một bản sao bổ sung trên hàm tạo trực tiếp. Tôi luôn gõ các cặp của mình để cung cấp cú pháp đơn giản.
Điều này cho thấy sự khác biệt (ví dụ của 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
Tôi khá chắc chắn rằng bản sao bổ sung sẽ bị xóa trong mọi trường hợp, nếu cài đặt tối ưu hóa của trình biên dịch đủ cao.
Bjorn Pollex

1
Tại sao bạn muốn dựa vào tối ưu hóa trình biên dịch cho chính xác?
sjbx

Tôi nhận được kết quả tương tự với cả hai phiên bản, và std::movechỉ bên trong insertvà / hoặc xung quanh những gì sẽ là một tài liệu tham khảo sample. Nó chỉ là khi tôi thay đổi std::map<int,Sample>để std::map<int,Sample const&>mà tôi giảm số lượng các đối tượng được xây dựng, và chỉ khi tôi xóa các constructor sao chép mà tôi loại bỏ tất cả các bản sao (rõ ràng). Sau khi thực hiện cả hai thay đổi đó, kết quả của tôi bao gồm một cuộc gọi đến hàm tạo mặc định và hai cuộc gọi đến hàm hủy cho cùng một đối tượng. Tôi nghĩ rằng tôi phải thiếu một cái gì đó. (g ++ 5.4.1, c ++ 11)
John P

FWIW Tôi đồng ý rằng tối ưu hóa và tính chính xác phải hoàn toàn độc lập, vì đây chính xác là loại mã bạn viết dưới dạng kiểm tra độ tỉnh táo sau khi các mức tối ưu hóa khác nhau tạo ra kết quả không nhất quán. Nói chung, tôi sẽ khuyên bạn emplacethay vì insertnếu bạn chỉ xây dựng một giá trị để chèn ngay lập tức (và bạn không muốn có thêm trường hợp.) Đó không phải là lĩnh vực chuyên môn của tôi, nếu tôi thậm chí có thể nói tôi có một, nhưng sao chép / di chuyển ngữ nghĩa được giới thiệu bởi C ++ 11 đã giúp tôi rất nhiều.
John P

Tôi tin rằng tôi đang gặp phải chính xác cùng một vấn đề và sau khi gỡ lỗi cho toàn bộ buổi tối, cuối cùng tôi đã đến đây.
lllllllllll

1

bắt đầu từ c ++ 11 chỉ cần sử dụng khởi tạo thống nhất cho các cặp. Vì vậy, thay vì:

std::make_pair(1, 2);

hoặc là

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

chỉ dùng

{1, 2};

{1, 2}có thể được sử dụng để khởi tạo một cặp, nhưng không cam kết cho loại cặp. Tức là khi sử dụng tự động, bạn phải cam kết với một loại trên RHS : auto p = std::pair{"Tokyo"s, 9.00};.
Markus
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.