2015-01-20 13 views
13

clang không biên dịch cuộc gọi thứ ba đến typeid bên dưới (xem live example). Nhưng tôi không thể nhìn thấy bất cứ điều gì trong §5.2.8 mà không cho phép điều này, đặc biệt khi chúng ta xem xét rằng biểu thức B::f không phải là một glvalue của loại lớp đa hình (xem đoạn 3). Ngoài ra, theo đoạn này, biểu thức B::f là toán hạng chưa được đánh giá, và như vậy, cuộc gọi typeid(B::f) sẽ biên dịch. Lưu ý rằng GCC không biên dịch bất kỳ cuộc gọi đến typeid dưới đây:typeid không hoạt động với chức năng thành viên không tĩnh

#include <iostream> 
#include <typeinfo> 

struct A{ int i; }; 
struct B{ int i; void f(); }; 

int main() 
{ 
    std::cout << typeid(A::i).name() << '\n'; 
    std::cout << typeid(B::i).name() << '\n'; 
    std::cout << typeid(B::f).name() << '\n'; 
} 
+0

@Yakk Tôi đã thử nghiệm với g + + và xác nhận rằng không có trong số ba compi le, sau đó chỉnh sửa câu hỏi để sửa lỗi phủ định kép. –

+0

Đưa '&' vào từ chúng khiến cho tất cả điều này hoạt động trên cả hai trình biên dịch, ví dụ: '& B :: i'. Nhưng, tôi đoán đó không phải là vấn đề! Tại sao họ không làm việc mà không có '&'? –

+0

@MarkB Cảm ơn bạn đã sửa. Như bạn có thể thấy tôi không có lệnh đầy đủ của ngôn ngữ tiếng Anh. – Belloc

Trả lời

11

Theo như tôi có thể nói clang là đúng, sử dụng một thành viên không tĩnh chỉ có giá trị trong bối cảnh unevaluated nếu nó là một thành viên dữ liệu . Vì vậy, có vẻ như gcc không chính xác cho hai trường hợp đầu tiên, nhưng gcc hoạt động chính xác trong trường hợp sizeofdecltype cũng có toán hạng không được đánh giá.

Từ draft C++11 standard phần 5.1.1[expr.prim.general]:

Một id-biểu thức biểu thị một thành viên dữ liệu không tĩnh hay không tĩnh hàm thành viên của một lớp chỉ có thể là sử dụng:

và bao gồm các viên đạn sau:

nếu biểu thức id đó biểu thị một thành viên dữ liệu không tĩnh và nó xuất hiện trong toán hạng chưa được đánh giá. [Ví dụ:

struct S { 
    int m; 
}; 
int i = sizeof(S::m); // OK 
int j = sizeof(S::m + 42); // OK 

dụ -end]

Phần còn lại của viên đạn không áp dụng, họ như sau:

  • như một phần của một truy cập thành viên lớp (5.2.5) trong đó biểu thức đối tượng đề cập đến lớp của thành viên hoặc một lớp có nguồn gốc từ lớp đó hoặc
  • để tạo thành con trỏ tới thành viên (5.3.1) hoặc
  • trong bộ khởi tạo mem cho một hàm tạo cho lớp đó hoặc cho lớp học có nguồn gốc từ lớp đó (12.6.2), hoặc
  • trong một cú đúp-hoặc-bằng-initializer cho một thành viên dữ liệu không tĩnh của lớp đó hoặc của một lớp dẫn xuất từ ​​lớp đó (12.6.2), hoặc

Chúng tôi biết rằng toán hạng không được đánh giá từ phần 5.2.8 có nội dung:

Khi typeid được áp dụng cho một biểu thức khác hơn là một glvalue của một loại lớp đa hình, [...] Biểu thức là một toán hạng unevaluated (khoản 5).

Chúng ta có thể nhìn thấy từ ngữ pháp mà một id-biểu hoặc là một không đủ tiêu chuẩn-id hoặc một có trình độ-id:

id-expression: 
    unqualified-id 
    qualified-id 

Cập nhật

Filed gcc bug report: typeid does not allow an id-expression that denotes a non-static data member.

+1

Darn - có câu trả lời gần như giống hệt nhau gần xong, nhưng đã làm việc trên định dạng cú pháp cho 'biểu thức chính' từ 5.1.1 khi bạn đăng ... –

1

typeid(A::i).name() không hoàn toàn làm những gì tôi nghĩ rằng nó sẽ làm. Tôi hy vọng nó là một con trỏ thành viên, nhưng nó thực sự chỉ là một int.

Để thấy điều này, chạy mã này:

#include <iostream> 
struct A{ int i; }; 
struct B{ int i; void f(void); }; 

template<typename T> 
void what_is_my_type() { 
    std:: cout << __PRETTY_FUNCTION__ << std:: endl; 
} 

int main() 
{ 
    what_is_my_type<decltype(&A::i)>(); // "void what_is_my_type() [T = int A::*]" 
    what_is_my_type<decltype(&B::i)>(); // "void what_is_my_type() [T = int B::*]" 
    what_is_my_type<decltype(&B::f)>(); // "void what_is_my_type() [T = void (B::*)()]" 

    what_is_my_type<decltype(A::i)>(); // "void what_is_my_type() [T = int]" 
    what_is_my_type<decltype(B::i)>(); // "void what_is_my_type() [T = int]" 
    // what_is_my_type<decltype(B::f)>();  // doesn't compile 

} 

tôi đã đưa ra trong một chú thích sau mỗi cuộc gọi.

Ba cuộc gọi đầu tiên hoạt động như mong đợi - cả ba công việc và thông tin loại bao gồm loại cấu trúc (A hoặc B) cũng như loại thành viên.

Ba trường hợp sau cùng khác nhau. Người cuối cùng thậm chí không biên dịch, và người đầu tiên chỉ cần in int. Tôi nghĩ đây là một đầu mối về những gì là sai. Có thể, cho một đặc biệt A hoặc B, để lấy địa chỉ của thành viên đó:

A a; 
int * x = &(a.i); 
*x = 32; 

nhưng nó là không có thể (hoặc thậm chí có ý nghĩa?) Để làm điều này:

B b; 
??? y = &(a.f); // what does this even mean? 

Cuối cùng, để nhấn mạnh rằng đây không phải là về con trỏ, hãy xem xét điều này:

A a; 
B b; 
int x = a.i; 
int y = b.i; 
??? z = b.f; // what would this mean? What's its type? 
Các vấn đề liên quan