C ++ 11 make_pair với các tham số mẫu được chỉ định không biên dịch


84

Tôi chỉ đang chơi với g ++ 4.7 (một trong những ảnh chụp nhanh sau này) với -std = c ++ 11 được bật. Tôi đã cố gắng biên dịch một số cơ sở mã hiện có của mình và một trường hợp không thành công khiến tôi hơi bối rối.

Tôi sẽ đánh giá cao nếu ai đó có thể giải thích những gì đang xảy ra.

Đây là mã:

#include <utility>
#include <iostream>
#include <vector>
#include <string>

int main ( )
{
    std::string s = "abc";

    // 1 ok
    std::pair < std::string, int > a = std::make_pair ( s, 7 );

    // 2 error on the next line
    std::pair < std::string, int > b = std::make_pair < std::string, int > ( s, 7 );

    // 3 ok
    std::pair < std::string, int > d = std::pair < std::string, int > ( s, 7 );

    return 0;
}

Tôi hiểu make_pair được có nghĩa là để được sử dụng như là (1) trường hợp (nếu tôi chỉ định các loại, sau đó tôi cũng có thể sử dụng (3)), nhưng tôi không hiểu tại sao nó thất bại trong trường hợp này.

Lỗi chính xác là:

test.cpp: In function ‘int main()’:
    test.cpp:11:83: error: no matching function for call to ‘make_pair(std::string&, int)’
    test.cpp:11:83: note: candidate is:
    In file included from /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/utility:72:0,
                 from test.cpp:1:
    /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note: template<class _T1, class _T2> constexpr std::pair<typename std::__decay_and_strip<_T1>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
    /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note:   template argument deduction/substitution failed:
    test.cpp:11:83: note:   cannot convert ‘s’ (type ‘std::string {aka std::basic_string<char>}’) to type ‘std::basic_string<char>&&’

Một lần nữa, câu hỏi ở đây chỉ là "chuyện gì đang xảy ra?" Tôi biết rằng tôi có thể khắc phục sự cố bằng cách xóa đặc tả mẫu, nhưng tôi chỉ muốn biết điều gì đang xảy ra ở đây.

  • g ++ 4.4 biên dịch mã này mà không có vấn đề gì.
  • Loại bỏ -std = c ++ 11 cũng biên dịch bằng mã mà không có vấn đề gì.

6
Một câu hỏi tuyệt vời. Tuy nhiên, một ví dụ khác về một thay đổi phá vỡ tinh vi trong C ++ 11, tương tự như thay đổi phá vỡ trong std::vectorxây dựng . Ít nhất điều này gây ra lỗi trình biên dịch và không phải là sự thay đổi âm thầm trong ngữ nghĩa.
James McNellis

1
Nếu tôi có một biến số nguyên i. Tôi muốn ghép nối với i và một đối tượng khác. Chính xác thì tôi nên gọi là makepair. 1) make_pair <* i, obj> 2) int && j = i; make_pair <j, obj>? Cả hai đều không hoạt động. Cách chính xác để làm điều đó là gì?
PHcoDer

Câu trả lời:


135

Đây không phải là cách std::make_pair được dự định sử dụng; bạn không được chỉ định rõ ràng các đối số mẫu.

C ++ 11 std::make_pairnhận hai đối số, kiểu T&&U&&, trong đó TU là các tham số kiểu mẫu. Về hiệu quả, nó trông như thế này (bỏ qua kiểu trả về):

template <typename T, typename U>
[return type] make_pair(T&& argT, U&& argU);

Khi bạn gọi std::make_pair và chỉ định rõ ràng các đối số kiểu mẫu, không có suy luận đối số nào diễn ra. Thay vào đó, các đối số kiểu được thay thế trực tiếp vào khai báo mẫu, tạo ra:

[return type] make_pair(std::string&& argT, int&& argU);

Lưu ý rằng cả hai loại tham số này đều là tham chiếu giá trị. Do đó, chúng chỉ có thể liên kết với các giá trị. Đây không phải là vấn đề đối với đối số thứ hai mà bạn vượt qua,7 , bởi vì đó là một biểu thức rvalue. stuy nhiên, là một biểu thức giá trị (nó không phải là biểu thức tạm thời và nó không được di chuyển). Điều này có nghĩa là mẫu hàm không phù hợp với các đối số của bạn, đó là lý do tại sao bạn gặp lỗi.

Vì vậy, tại sao nó hoạt động khi bạn không chỉ định rõ ràng những gì TUcó trong danh sách đối số mẫu? Tóm lại, các tham số tham chiếu rvalue là đặc biệt trong các mẫu. Một phần do tính năng ngôn ngữ được gọi là thu gọn tham chiếu , một tham số tham chiếu rvalue thuộc loạiA&& , trong đó Alà tham số kiểu mẫu, có thể liên kết với bất kỳ loại nào A.

Nó không quan trọng cho dù A là lvalue, rvalue, const-đủ điều kiện, đủ điều kiện dễ bay hơi hay không đủ tiêu chuẩn, một A&&có thể liên kết với đối tượng đó (một lần nữa, nếu và chỉ khi Abản thân nó là một tham số mẫu).

Trong ví dụ của bạn, chúng tôi thực hiện cuộc gọi:

make_pair(s, 7)

Đây, slà giá trị của loại std::string7 là của kiểu int. Vì bạn không chỉ định đối số mẫu cho mẫu hàm, nên việc suy diễn đối số mẫu được thực hiện để tìm ra đối số là gì.

Để ràng buộc s, một giá trị, để T&&, trình biên dịch suy luận Tstd::string&, tạo ra một đối số kiểu std::string& &&. Tuy nhiên, không có tham chiếu nào đến tham chiếu, vì vậy "tham chiếu kép" này bị thu gọn để trở thành std::string&. slà một trận đấu.

Thật đơn giản để liên kết 7với U&&: trình biên dịch có thể suy ra Uint, tạo ra một tham số kiểu int&&, liên kết thành công với 7vì nó là một giá trị.

Có rất nhiều điều tinh tế với các tính năng ngôn ngữ mới này, nhưng nếu bạn tuân theo một quy tắc đơn giản, thì khá dễ dàng:

Nếu một đối số mẫu có thể được suy ra từ các đối số của hàm, hãy để nó được suy ra. Không cung cấp đối số một cách rõ ràng trừ khi bạn hoàn toàn phải làm.

Hãy để trình biên dịch thực hiện công việc khó khăn và 99,9% thời gian đó sẽ là chính xác những gì bạn muốn. Khi nó không như bạn muốn, bạn sẽ thường gặp lỗi biên dịch rất dễ xác định và sửa chữa.


6
Đây là một lời giải thích rất hay và toàn diện. Cảm ơn bạn!
vmpstr

1
@James - đó có phải là "một quy tắc đơn giản" từ một bài báo hoặc câu trả lời khác mà tôi nên đọc không?
Michael Burr

4
@MichaelBurr: Không, tôi vừa mới bịa ra. :-) Vì vậy, tôi hy vọng đó là sự thật! Tôi nghĩ đó là sự thật ... quy tắc đó phù hợp với tôi mọi lúc.
James McNellis

1
@James: cảm ơn. 'Hộp trích dẫn' xung quanh nó khiến tôi nghĩ rằng nó có thể đã được viết một cái gì đó ban đầu ở nơi khác. Câu trả lời này thực sự giàu thông tin và tôi chỉ muốn đảm bảo rằng tôi không bỏ lỡ thứ gì đó ở nơi khác.
Michael Burr

2
Điều này cũng áp dụng cho các bộ giá trị?
Ferruccio
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.