2014-11-03 15 views
10

Tại sao cuộc gọi hàm đầu tiên (cm(car);) liên kết với hàm đầu tiên?Tại sao cuộc gọi hàm đầu tiên liên kết với hàm đầu tiên?

Tôi hiểu rằng cuộc gọi thứ hai bị ràng buộc với chức năng thứ hai vì nó không phải là mẫu, mặc dù cả hai đều là đối sánh hoàn hảo.

Nếu chức năng đầu tiên được định nghĩa là không mẫu với chiều dài mảng cố định, như:

void cm(const char (&h)[8]) {cout << "const char (&)[8]" << endl;} 

hơn nữa nó được chọn trong một giây (cuộc gọi thứ hai sẽ là mơ hồ như vậy).

Code:

template<size_t N> void cm(const char (&h)[N]) 
    {std::cout << " const (&)[N] " << endl;} 

void cm(const char * h) 
    {cout << " const char * " << endl;} 

int main() 
{ 
    char car[] = "errqweq"; 
    const char ccar[] = "errqweq"; 
    cm(car); 
    cm(ccar); 
} 

Output:

const (&)[N] 
const char * 

Trả lời

2

Bởi vì chuỗi "errqweq" trực tiếp bằng văn bản trong các mã được đọc chỉ vì nó là, tại thời gian chạy, trong một phần của ký ức " được bảo vệ "vì nó được quản lý như một hằng số.

Chỉ vào nó bằng cách sử dụng const char* ccar; hoặc const char ccar[]; là chính xác. Bạn đang trỏ đến bộ nhớ giữ bản gốc "errqweq" với trình định danh const: trình biên dịch đảm bảo rằng chuỗi sẽ không bị sửa đổi.

Nhưng nhìn vào: char car[] = "errqweq";

Để cung cấp cho bạn một bộ đệm modificable (như bạn đang yêu cầu mà không có sự sửa đổi lần const) trình biên dịch tạo một mảng của 8 yếu tố (7 chars + \ 0) trên stack sao chép trong đó (ví dụ: intializing nó) chuỗi "errqweq".

Vì vậy, cuộc gọi đầu tiên đang sử dụng một đối số char buffer[8] được chuyển đổi an toàn thành const char buffer[8]. Rõ ràng kích thước cố định của mảng cho phù hợp nhất với mẫu thay vì liên kết "yếu" hơn với hàm đòi hỏi "chỉ" một con trỏ không đổi.

+0

Huh, tôi không bao giờ biết rằng 'const char []' sẽ làm điều đó với một chuỗi chữ, tôi nghĩ nó sẽ hoạt động giống như 'char []' trong trường hợp này. –

+0

Luận điểm trong đoạn cuối của câu trả lời là không đúng, phải không? – Columbo

4

Cuộc gọi đầu tiên chọn chuyên môn mẫu chức năng - bởi vì nó phù hợp hơn.
Chúng ta hãy đặt tên cả hai quá tải:

template<size_t N> void cm(const char (&h)[N]) // (1) - the specialization 
    {std::cout << " const (&)[N] " << endl;} 

void cm(const char * h)       // (2) 
    {cout << " const char * " << endl;} 

Đối với (1), car liên kết với một tài liệu tham khảo. Đó là chuyển đổi nhận dạng . Đối với (2), sau khi chuyển đổi mảng-thành-con trỏ của car, trong đó mang lại char* , một chuyển đổi tiêu chuẩn phải được thực hiện để char* trở thành
char const*. Mà bây giờ được gọi này:

tiêu chuẩn chuyển đổi chuỗi S1 là một chuỗi chuyển đổi tốt hơn so với chuỗi tiêu chuẩn chuyển đổi S2 nếu

  • S1 là một dãy con đúng S2 (so sánh trình tự chuyển đổi theo hình thức kinh điển được xác định bởi 13.3.3.1.1, không bao gồm bất kỳ Chuyển đổi Lvalue nào; trình tự chuyển đổi danh tính được coi là một dãy con của bất kỳ chuyển đổi phi sắc chuỗi) hoặc, nếu không muốn nói rằng,
  • [...]

Một mảng-to-con trỏ Chuyển đổi là Chuyển đổi Lvalue, vì vậy nó không được xem xét ở đây - giống như trong ví dụ thứ hai. Chuyển đổi bằng cấp có danh mục riêng mặc dù: Điều chỉnh tiêu chuẩn. Do đó việc chuyển đổi thành tham số của (1) là một chuỗi chuyển đổi thành tham số của (2): đầu tiên là chuyển đổi nhận dạng và chuyển đổi thứ hai, và theo đoạn trên, chuyển đổi nhận dạng là một chuỗi bất kỳ chuyển đổi không nhận dạng nào. Vì vậy, (1) được chọn.

Như bạn đã tự đề cập, trong trường hợp thứ hai, các chuyển đổi đều tốt như nhau; Trích dẫn ở trên không hoạt động vì chuyển đổi sang tham số (2) s không phải là một chuỗi chuyển đổi cho tham số của (1). Do đó, [over.match.best]/1 được áp dụng.

Với những định nghĩa, một chức năng hữu hiệu đối F1 được định nghĩa là một hàm tốt hơn so với một chức năng hữu hiệu đối F2 nếu cho tất cả các đối i, ICSI (F1) không phải là một chuỗi chuyển đổi tồi tệ hơn ICSI (F2), và sau đó

  • đối với một số tranh cãi j, ICSj (F1) là một chuỗi chuyển đổi tốt hơn ICSj (F2), hoặc, nếu không muốn nói rằng,
  • bối cảnh là một khởi bởi chuyển đổi người dùng định nghĩa [...] hoặc, nếu không,
  • F1 là một hàm phi mẫu và F2 là một hàm mẫu chuyên môn,

Vì vậy, (2) một được chọn. Nếu mẫu chức năng không phải là mẫu nhưng hàm có thông số
char const (&)[8] cuộc gọi sẽ không rõ ràng as Clang correctly says.


[over.ics.ref]/1:

Khi một tham số của kiểu tham chiếu liên kết trực tiếp (8.5.3) để một biểu thức luận , trình tự chuyển đổi ngầm là định danh chuyển đổi, trừ khi biểu thức đối số có loại là lớp bắt nguồn của loại tham số, trong trường hợp này, trình tự chuyển đổi tiềm ẩn là chuyển đổi từ cơ sở sang nguồn gốc (13.3.3.1).

[dcl.init.ref]/5 (mà là ở 8.5.3):

Trong tất cả các trường hợp ngoại trừ người cuối cùng (ví dụ, tạo và khởi tạo một tạm thời từ biểu thức khởi tạo) , tham chiếu được gọi là liên kết trực tiếp với biểu thức khởi tạo.


[conv.array]:

Một vế trái hoặc rvalue kiểu “mảng của N T” hoặc “mảng không rõ ràng buộc của T” có thể được chuyển đổi sang một prvalue loại "con trỏ đến T". Kết quả là một con trỏ đến phần tử đầu tiên của mảng.

T có thể đủ điều kiện cv và do đó loại điểm được chọn. Tại đây, T chỉ là char, vì vậy con trỏ có dạng con trỏ là char =>char*.

+0

"liên kết trực tiếp" dường như không được xác định bởi 8.5.3. Bạn đang nói rằng ràng buộc một mảng không const để tham chiếu const được tính là "liên kết trực tiếp"? –

+0

@MattMcNabb Vì có vẻ như điều này có thể gây nhầm lẫn cho nhiều người hơn tôi đã thêm một chú thích. :) (Nó được định nghĩa, xem chú thích của tôi) – Columbo

+0

AH Tôi đã bỏ qua điều đó. Vì vậy, nó có nghĩa là bất kỳ ràng buộc tham chiếu nào không liên quan đến tạm thời. –

Các vấn đề liên quan