2013-08-06 20 views
14

Cả clang và gcc đều chấp nhận mã sau và chọn A::operator B*.Nên khởi tạo bởi chức năng chuyển đổi là mơ hồ khi hai ứng cử viên có cùng một trình độ cv?

struct B 
{ 
}; 

struct A : B 
{ 
    operator A*(); 
    operator B*(); 
}; 

A a; 
void* x = a; 

Đọc của tôi về tiêu chuẩn - câu cụ thể được đánh dấu dưới chữ in đậm - cho thấy chuyển đổi này nên mơ hồ.

Cả A::operator A*A::operator B* là ứng cử viên cho giải quyết tình trạng quá tải vì A*B* đều chuyển đổi thành void* qua một chuyển đổi tiêu chuẩn. Bởi vì tham số đối tượng ngụ ý A& là đối số duy nhất, chỉ có chuỗi chuyển đổi chuyển đổi từ đối số ngụ ý đối tượng sang tham số đối tượng ngụ ý được xem xét - loại được sinh ra bởi hàm chuyển đổi bị bỏ qua. Trong cả hai trường hợp, tham số đối tượng ngụ ý là kiểu biểu thức khởi tạo A và tham số đối tượng ngụ ý là A&. Nếu cả hai chuỗi chuyển đổi giống nhau, không có cách nào để phân biệt giữa hai ứng cử viên.

8,5 Initializers [dcl.init]

Ngữ nghĩa của initializers như sau. Loại đích là loại đối tượng hoặc tham chiếu là được khởi tạo và loại nguồn là loại biểu thức khởi tạo.

- Nếu loại đích là một [tài liệu tham khảo/mảng/lớp ...] [đã xóa chi tiết không áp dụng đối với kịch bản này]

- Ngược lại, nếu các loại nguồn là một (có thể cv-trình độ) loại lớp, chức năng chuyển đổi được xem xét. Các chức năng chuyển đổi áp dụng được liệt kê (13.3.1.5), và tốt nhất được chọn thông qua quá tải độ phân giải (13.3). Chuyển đổi do người dùng xác định để chọn được gọi để chuyển đổi biểu thức khởi tạo thành đối tượng đang được khởi tạo. Nếu việc chuyển đổi không thể thực hiện hoặc không rõ ràng, việc khởi tạo là không đúng định dạng.

13.3.1.5 Khởi tạo bởi chức năng chuyển đổi [over.match.conv]

Theo các điều kiện quy định tại 8.5, như một phần của một khởi của một đối tượng kiểu nonclass, một chức năng chuyển đổi có thể được viện dẫn để chuyển đổi biểu thức khởi tạo của loại lớp thành loại đối tượng đang được khởi tạo . Độ phân giải quá tải được sử dụng để chọn hàm chuyển đổi cần gọi. Giả sử rằng “CV1 T” là kiểu của đối tượng được khởi tạo, và “cv S” là kiểu của biểu thức khởi tạo, với S một loại lớp, các chức năng ứng cử viên được lựa chọn như sau:

- Các hàm chuyển đổi của S và các lớp cơ sở của nó được xem xét. Các chuyển đổi không rõ ràng các chức năng không bị ẩn trong S và loại T hoặc loại có thể được chuyển thành loại T qua chuỗi chuyển đổi chuẩn (13.3.3.1.1) là hàm ứng viên. Để khởi tạo trực tiếp, những hàm chuyển đổi rõ ràng không bị ẩn trong S và kiểu T hoặc loại có thể là được chuyển thành loại T với chuyển đổi đủ điều kiện (4.4) cũng là hàm ứng viên. Chuyển đổi các hàm trả về loại có đủ điều kiện cv được coi là mang lại phiên bản không đủ tiêu chuẩn của loại đó cho quá trình chọn chức năng ứng cử viên này. Hàm chuyển đổi trả về “giá trị trả về cv2 X” trả về giá trị hoặc xvalues, tùy thuộc vào loại tham chiếu, thuộc loại “cv2 X” và do đó được coi là mang lại X cho quá trình chọn các hàm ứng cử viên.

Danh sách đối số có một đối số, là biểu thức khởi tạo. [Lưu ý: Đối số này sẽ là so với tham số đối tượng ẩn của hàm chuyển đổi. —end note]

Điều này có mơ hồ theo tiêu chuẩn không?

EDIT: lưu ý rằng đây là một câu hỏi tương tự, nhưng không giống như Distinguishing between user-defined conversion sequences by the initial standard conversion sequence

Sự khác biệt là trong ví dụ của tôi cả hai chức năng chuyển đổi có trình độ tương tự.

+0

Cả hai chức năng chuyển đổi trở về một con trỏ đến địa chỉ cơ sở cùng một ví dụ mà chuyển đổi thành 'void *' gây ra. Thử đa thừa kế và toán tử chuyển đổi bổ sung và chuyển đổi sẽ không rõ ràng. –

+0

@CaptainObvlious Làm thế nào để bạn biết những gì họ quay trở lại? [Họ thậm chí không cần phải được thực hiện để kiểm tra câu hỏi này] (http://coliru.stacked-crooked.com/view?id=ad1ba686b7a271079f6ca6c5e91f5c02-e1204655eaff68246b392dc70c5a32c9). – Casey

+0

@Casey Vì toán tử chuyển đổi có loại trả về xác định những gì chúng trả về. –

Trả lời

9

TLDR: Khi mọi thứ khác bằng nhau, độ phân giải quá tải sẽ phá vỡ tie theo đó hàm chuyển đổi có chuyển đổi tốt nhất từ ​​giá trị trả về của nó thành loại mục tiêu.


Tất cả các tham chiếu đến ISO/IEC 14882: 2011 (C++ 11). Hành vi của quá trình khởi tạo:

void* x = a; 

được định nghĩa như sau. Đầu tiên, này là khởi tạo như được mô tả trong 8.5 Trình khởi tạo [dcl.init] và phù hợp với ngữ pháp được mô tả trong p1. Kể từ kiểu đích void* là một loại phi giai cấp, và các loại nguồn A một kiểu lớp, khởi tạo cụ thể này có dạng mô tả trong 8,5 p16, đạn 7:

Ngược lại, nếu nguồn loại là một loại lớp (có thể cv đủ điều kiện), các hàm chuyển đổi được xem xét. Các chức năng chuyển đổi áp dụng được liệt kê (13.3.1.5), và chức năng tốt nhất được chọn thông qua độ phân giải quá tải (13.3). Chuyển đổi do người dùng định nghĩa để chọn được gọi để chuyển đổi biểu thức khởi tạo thành đối tượng đang được khởi tạo. Nếu việc chuyển đổi không thể được thực hiện hoặc không rõ ràng, thì việc khởi tạo sẽ bị hình thành không đúng.

Các "liệt kê các chức năng chuyển đổi áp dụng" là chi tiết trong 13.3.1.5 p1:

Theo các điều kiện quy định tại 8.5, như một phần của một khởi của một đối tượng kiểu nonclass, một chuyển đổi chức năng có thể được gọi để chuyển đổi một biểu thức khởi tạo của loại lớp thành loại đối tượng đang được khởi tạo . Độ phân giải quá tải được sử dụng để chọn hàm chuyển đổi cần gọi.Giả sử rằng “CV1 T” là kiểu của đối tượng được khởi tạo, và “cv S” là kiểu của biểu thức khởi tạo, với S một loại lớp, các chức năng ứng cử viên được lựa chọn như sau:

  • Các hàm chuyển đổi của S và các lớp cơ sở của nó được xem xét. Các hàm chuyển đổi không rõ ràng không bị ẩn trong S và loại lợi T hoặc loại có thể được chuyển đổi thành loại T qua chuỗi chuyển đổi chuẩn (13.3.3.1.1) là hàm ứng cử viên. Để khởi tạo trực tiếp, các hàm chuyển đổi rõ ràng không được ẩn trong S và kiểu T hoặc loại có thể là được chuyển thành loại T với chuyển đổi đủ điều kiện (4.4) cũng là hàm ứng viên. Chuyển đổi các hàm trả về loại có đủ điều kiện cv được coi là mang lại phiên bản không đủ tiêu chuẩn của loại đó cho quá trình chọn chức năng ứng cử viên này. Hàm chuyển đổi trả về “giá trị trả về cv2 X” trả về giá trị hoặc xvalues, tùy thuộc vào loại tham chiếu, thuộc loại “cv2 X” và do đó được coi là mang lại X cho quá trình chọn các hàm ứng cử viên.

Lưu ý rằng cả hai A::operator A*()A::operator B*() là chức năng ứng cử viên, vì cả hai A*B* là mui trần để void* mỗi 4.10p2: "Một prvalue kiểu‘con trỏ đến cv T’, nơi T là một loại đối tượng , có thể được chuyển đổi thành giá trị của loại "con trỏ đến cv void". " Cho rằng cả hai chức năng là ứng viên, độ phân giải quá tải phải chọn giữa chúng.

độ phân giải quá tải được mô tả trong 13,3 [over.match]. p2 khẳng định:

độ phân giải quá tải chọn chức năng để gọi trong bảy bối cảnh khác biệt trong ngôn ngữ:

...

  • gọi của một hàm chuyển đổi cho khởi của một đối tượng của một nonclass nhập từ biểu thức loại lớp (13.3.1.5)

...

Mỗi ngữ cảnh này định nghĩa tập hợp các hàm ứng cử viên và danh sách các đối số theo cách riêng của nó. Nhưng, một khi các chức năng ứng cử viên và danh sách đối số đã được xác định, việc lựa chọn các chức năng tốt nhất là như nhau trong mọi trường hợp:

  • Đầu tiên, một tập hợp con của các chức năng ứng cử viên (những người có số lượng thích hợp của các đối số và đáp ứng một số điều kiện khác) được chọn để tạo thành một tập hợp các chức năng khả thi (13.3.2).

  • Sau đó, chức năng khả thi tốt nhất được chọn dựa trên chuỗi chuyển đổi ngầm định (13.3.3.1) cần để đối sánh từng đối số với thông số tương ứng của mỗi hàm khả thi.

Chức năng nào trong hai chức năng của chúng tôi là khả thi? 13.3.2 [quá lớn.khả thi] p1:

Từ tập các chức năng ứng cử viên xây dựng cho một bối cảnh nhất định (13.3.1), một tập hợp các hàm khả thi được chọn, từ đó chức năng tốt nhất sẽ được lựa chọn bằng cách so sánh trình tự chuyển đổi đối số cho các phù hợp nhất (13.3.3).

Các yêu cầu được thể hiện trong p2:

Trước tiên, để có một hàm khả thi, một chức năng ứng cử viên phải có đủ các thông số thống nhất về số lượng với các đối số trong danh sách.

và p3:

Thứ hai, cho F là một hàm khả thi, có trách nhiệm tồn tại cho mỗi đối số một chuỗi chuyển đổi ngầm (13.3.3.1) có thể chuyển đổi lập luận cho rằng để các tham số tương ứng của F.

Cả hai yêu cầu đều được đáp ứng một cách nhỏ gọn bởi các hàm chuyển đổi của chúng tôi: chúng có một đối số duy nhất (ẩn) cùng loại với biểu thức khởi tạo a.

Xác định tốt nhất các chức năng khả thi được mô tả trong 13.3.3 [over.match.best]. Nó định nghĩa một số hình thức để mô tả các chuỗi chuyển đổi , các chuỗi hoạt động cần thiết để chuyển đổi từ các kiểu đối số chức năng thực tế thành các kiểu tham số hàm chính thức. Trong trường hợp các hàm chuyển đổi của chúng ta, cả hai đều có chính xác một tham số có kiểu là chính xác đối số thực tế, do đó, "chuỗi chuyển đổi" tương ứng với từng quá tải là chuỗi nhận dạng. Chúng được phân biệt đối xử bởi ngôn ngữ trong p1:

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 chức năng 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 hơn ICSi(F2), và sau đó

  • đối với một số lập luận 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,

  • ngữ cảnh là khởi tạo bởi chuyển đổi do người dùng xác định (xem 8.5, 13.3.1.5 và 13.3.1.6) và trình tự chuyển đổi chuẩn từ kiểu trả về F1 thành loại đích (nghĩa là loại thực thể được khởi tạo) là chuỗi chuyển đổi tốt hơn chuỗi chuyển đổi chuẩn từ loại trả về là F2 đối với loại đích.

Còn viên đạn cuối cùng thì sao? Liệu một trong những tình trạng quá tải của chúng tôi có một chuỗi chuyển đổi tiêu chuẩn tốt hơn từ kiểu trả về của nó thành void*?

13.3.3.2 Ranking Implicit Trình tự chuyển đổi [over.ics.rank] bang p4 tại điểm viên đạn thứ hai:

Nếu lớp B có nguồn gốc trực tiếp hoặc gián tiếp từ lớp A, chuyển đổi B*-A* là tốt hơn so với chuyển đổi B* để void* và chuyển đổi A* thành void* tốt hơn chuyển đổi B* thành void*.

Đây chính là trường hợp của OP, ngoại trừ các tên AB được đảo ngược. Nghị quyết tình trạng quá tải trên hai toán tử chuyển đổi của OP được giải quyết có lợi cho A::operator B*() kể từ khi quy tắc trích dẫn làm cho các chuỗi chuyển đổi B*void* tốt hơn so với A*void*

+0

Điều này đề cập đến việc xếp hạng các chuỗi chuyển đổi từ (các) loại đối số đến (các) loại tham số của các hàm ứng cử viên trong độ phân giải quá tải. Trong trường hợp này, không phải 'A *' hoặc 'B *' là các kiểu đối số và 'void *' không phải là kiểu tham số. Do đó không có lý do nào để so sánh các chuỗi chuyển đổi này, theo đọc của tôi về tiêu chuẩn. – willj

+0

@willj Đó là những gì tôi nhận được vì không hiển thị công việc của tôi. Có toàn bộ đối số cho bạn. TLDR: Khi mọi thứ khác bằng nhau, độ phân giải quá tải sẽ phá vỡ tie theo đó hàm chuyển đổi có chuyển đổi * tốt nhất * từ giá trị trả về của nó thành kiểu đích. – Casey

+0

Tôi hơi bối rối về tiêu chuẩn C++ ở đây. Nếu B có nguồn gốc từ A, thì tôi sẽ xem xét việc chuyển đổi B * thành void * để tốt hơn chuyển đổi A * thành void *. Sau khi tất cả, B là loại cụ thể hơn A. Như là, tiêu chuẩn về cơ bản "hạ cấp" các đối tượng trong câu hỏi càng nhiều càng tốt, trước khi đi vào khoảng trống. –

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