Tại sao C ++ không cho phép cấu trúc ẩn danh?


92

Một số trình biên dịch C ++ cho phép kết hợp và cấu trúc ẩn danh như một phần mở rộng cho C ++ tiêu chuẩn. Đó là một chút đường cú pháp đôi khi rất hữu ích.

Cơ sở lý luận nào ngăn điều này trở thành một phần của tiêu chuẩn? Có rào cản kỹ thuật nào không? Một triết học? Hay chỉ là không đủ nhu cầu để biện minh cho nó?

Đây là một ví dụ về những gì tôi đang nói về:

struct vector3 {
  union {
    struct {
      float x;
      float y;
      float z;
    };
    float v[3];
  };
};

Trình biên dịch của tôi sẽ chấp nhận điều này, nhưng nó cảnh báo rằng "struct / union không tên" là một phần mở rộng không chuẩn cho C ++ .


3
Rõ ràng là có một số nhầm lẫn về ý bạn. Bạn có thể vui lòng cung cấp một ví dụ về mã chỉ biên dịch do phần mở rộng của trình biên dịch không?
Rob Kennedy

74
Lưu ý rằng có hai khái niệm, nghe có vẻ giống nhau, nhưng rất khác nhau: cấu trúc không têncấu trúc ẩn danh . Đầu tiên là cái này, mà C ++ hỗ trợ: struct { int i; } a; a.i = 0;(kiểu không có tên). Thứ hai là cái này, cái mà C ++ không hỗ trợ: struct { int i; }; i = 0;(kiểu không có tên, và nó thoát vào phạm vi xung quanh). C ++, tuy nhiên, không hỗ trợ cả hai vô danh và vô danh công đoàn .
Johannes Schaub - litb

Điều này trông giống như thư viện vectơ VMMLib khá thú vị. Tôi tin rằng vấn đề là liên minh chứa một cấu trúc không tên, nhưng tôi không chắc.
greyfade

1
FWIW Đó là "dị biệt", không phải "vô danh", và các công đoàn được hỗ trợ như litb nói. stackoverflow.com/q/14248044/560648
Lightness Races ở Orbit

1
@AdrianMcCarthy: Điều đó tốt (FSVO "tốt"; trình biên dịch pesky khó hiểu), nhưng chính xác "không tên" là một khái niệm tiêu chuẩn, không liên quan.
Các cuộc đua ánh sáng trong quỹ đạo

Câu trả lời:


50

Như những người khác đã chỉ ra kết hợp ẩn danh được phép trong C ++ tiêu chuẩn, nhưng cấu trúc ẩn danh thì không.

Lý do cho điều này là C hỗ trợ kết hợp ẩn danh nhưng không hỗ trợ cấu trúc ẩn danh *, vì vậy C ++ hỗ trợ cái trước để tương thích nhưng không hỗ trợ cái sau vì nó không cần thiết để tương thích.

Hơn nữa, không có nhiều công dụng đối với các cấu trúc ẩn danh trong C ++. Việc sử dụng bạn chứng minh, để có một cấu trúc có chứa ba nổi có thể được gọi bằng cách .v[i], hay .x, .y.z, tôi tin rằng kết quả trong hành vi undefined trong C ++. C ++ không cho phép bạn viết cho một thành viên của liên minh, giả sử .v[1], và sau đó đọc từ một thành viên khác, nói .y. Mặc dù mã thực hiện điều này không phải là hiếm nhưng nó không thực sự được xác định rõ.

Cơ sở vật chất của C ++ cho các kiểu do người dùng định nghĩa cung cấp các giải pháp thay thế. Ví dụ:

struct vector3 {
  float v[3];
  float &operator[] (int i) { return v[i]; }
  float &x() { return v[0]; }
  float &y() { return v[1]; }
  float &z() { return v[2]; }
};

* C11 dường như thêm các cấu trúc ẩn danh, do đó, một bản sửa đổi trong tương lai cho C ++ có thể thêm chúng.


2
+1: Ví dụ của tôi dựa trên hành vi không xác định trong C ++ - điều mà tôi đã không biết khi viết câu hỏi.
Adrian McCarthy

2
"C ++ không cho phép bạn viết cho một thành viên của liên minh [...] và sau đó đọc từ thành viên khác" - trừ khi các thành viên đã nói là các đối tượng bố cục chuẩn và chia sẻ một chuỗi ban đầu chung của các thành viên của riêng họ, và bạn ' đang viết / đọc các thành viên của họ trong trình tự ban đầu thông thường đã nói. Điều đó được cho phép (nghĩa là được xác định).
underscore_d

5
@underscore_d: Có, nếu các loại là bố cục chuẩn với một trình tự ban đầu chung. Tuy nhiên, một cấu trúc không bao giờ có thể đặt bí danh với một mảng theo cách này, bởi vì các quy tắc "chuỗi ban đầu chung" của C ++ nói rằng một chuỗi ban đầu chung chỉ có thể nằm giữa các cấu trúc . Mảng không được đề cập, vì vậy chúng không thể có bí danh như thế này.
Nicol Bolas

@NicolBolas Ồ, haha ​​- tin tôi đi - tôi đã ước nhiều lần rằng các mảng và các nguyên thủy khác được bao gồm trong khoản trợ cấp này! Nhưng tôi chưa nghĩ nhiều về những hạn chế thực tế có thể xảy ra đối với điều đó, vì vậy có lẽ nó không đơn giản như hiện tại. Nhận xét của tôi là tổng quát hơn nhưng có thể đã liều ngụ ý bởi thiếu sót mà tôi nghĩ mảng đã được bao gồm trong này, do đó, nhờ thêm rằng trong.
underscore_d

"Lý do cho điều này là C hỗ trợ các công đoàn ẩn danh nhưng không phải các cấu trúc ẩn danh" - Không. Chú thích của bạn làm rõ rằng bạn đã nói về C99 hoặc trước đó ở đây. Thuật ngữ "công đoàn ẩn danh" không xuất hiện ở bất kỳ đâu trong tiêu chuẩn C99. GCC tuyên bố trong một chẩn đoán (với tùy chọn -std = c99 -pedantic) rằng "ISO C99 không hỗ trợ cấu trúc / liên hiệp không có tên". Tiêu chuẩn không đề cập đến bất kỳ điều gì về các thành viên không có tên ngoài các trường bit không được đặt tên. Tôi không hoàn toàn chắc chắn liệu khai báo cấu trúc có phải là khai báo hay không, nhưng nếu có, các liên kết ẩn danh là một vi phạm ràng buộc trên 6.7p2, tốt nhất là không xác định.

21

Tôi sẽ nói, bạn có thể xóa vector3tờ khai của mình bằng cách sử dụngunion

union vector3 {
  struct { float x, y, z; } ;
  float v[3] ;
} ;

Chắc chắn, cấu trúc ẩn danh một phần mở rộng của MSVC . Nhưng ISO C11 cho phép nó ngay bây giờ, và gcc cho phép nó , và trình biên dịch llvm của Apple cũng vậy.

Tại sao trong C11 mà không phải C ++ 11? Tôi không chắc, nhưng thực tế nói hầu hết (gcc ++, MSVC ++ và trình biên dịch C ++ của Apple) các trình biên dịch C ++ hỗ trợ chúng.


1
+1 để biết thông tin cập nhật. Lý do tôi có cấu trúc bên ngoài là vì "mã thực" cũng có các phương thức.
Adrian McCarthy

Điều duy nhất bạn không thể làm với liên hợp là có các thành viên dữ liệu tĩnh hoặc sử dụng kế thừa .
bobobobo

2
Cảm ơn. Tôi không bao giờ mới một liên minh có thể được sử dụng như một cấu trúc hoặc một lớp.
Adrian McCarthy

Tôi biết Sun studio không hỗ trợ cấu trúc ẩn danh trước C ++ 11 theo mặc định. Nếu bạn đang viết mã nền tảng chéo và trình biên dịch không được nâng cấp lên C + 11 thì không sử dụng cấu trúc ẩn danh.
bắt đầu từ

6

Không chắc chắn những gì bạn có ý nghĩa. Phần 9.5 của đặc tả C ++, điều khoản 2:

Một liên minh của biểu mẫu

union { member-specification } ;

được gọi là công đoàn ẩn danh; nó định nghĩa một đối tượng không tên của kiểu không tên.

Bạn cũng có thể làm những việc như sau:

void foo()
{
  typedef
  struct { // unnamed, is that what you mean by anonymous?
    int a;
    char b;
  } MyStructType; // this is more of a "C" style, but valid C++ nonetheless

  struct { // an anonymous struct, not even typedef'd
    double x;
    double y;
  } point = { 1.0, 3.4 };
}

Không phải lúc nào cũng hữu ích ... mặc dù đôi khi hữu ích trong các định nghĩa macro khó hiểu.


11
-1 vì nó nói rằng nó xác định một cấu trúc ẩn danh. Xem nhận xét ở trên về câu hỏi - bạn đang xác định một cấu trúc không tên, không phải một cấu trúc ẩn danh.
Johannes Schaub - litb

1

Công đoàn có thể ẩn danh; xem Tiêu chuẩn, 9.5 đoạn 2.

Bạn thấy một cấu trúc hoặc lớp ẩn danh đáp ứng được mục đích gì? Trước khi suy đoán tại sao điều gì đó không có trong Tiêu chuẩn, tôi muốn có một số ý tưởng tại sao nó nên như vậy và tôi không thấy sử dụng cho một cấu trúc ẩn danh.


1

Dựa trên bản chỉnh sửa, các nhận xét và bài viết MSDN này: Các cấu trúc ẩn danh , tôi sẽ đánh liều đoán - nó không phù hợp với khái niệm đóng gói. Tôi sẽ không mong đợi một thành viên của một lớp gây rối với không gian tên lớp của tôi ngoài việc chỉ thêm một thành viên. Hơn nữa, những thay đổi đối với cấu trúc ẩn danh có thể ảnh hưởng đến lớp của tôi mà không được phép.


1
Do cách cấu trúc / liên hiệp ẩn danh được tạo (cú pháp nội tuyến đặc biệt, không thể bị ẩn trừ macro), bạn không thể ngạc nhiên rằng một số thành viên bạn đang sử dụng là thành viên ẩn danh. Vì vậy, tôi không nghĩ lý luận này có ý nghĩa gì. Lý do thực tế là các liên hiệp ẩn danh được hỗ trợ trong C ++, chỉ để tương thích với C. C không hỗ trợ cấu trúc ẩn danh (cho đến C11) và vì vậy C ++ cũng không.
bames53

1

Ma cua ban

union {
  struct {
    float x;
    float y;
    float z;
  };
  float v[3];
};

giống như

union Foo {
   int;
   float v[3];
};

mà chắc chắn không hợp lệ (trong C99 trở về trước).

Lý do có lẽ là để đơn giản hóa việc phân tích cú pháp (trong C), bởi vì trong trường hợp đó bạn chỉ cần kiểm tra xem phần struct / union chỉ có "câu lệnh khai báo" như

Type field;

Điều đó nói rằng, gcc và "các trình biên dịch khác" hỗ trợ các trường không tên như một phần mở rộng.

Chỉnh sửa: Các cấu trúc ẩn danh hiện được hỗ trợ chính thức trong C11 (§6.7.2.1 / 13).


5
Từ quan điểm phân tích cú pháp, tôi không nghĩ rằng điều đó union { ... }có gì khác biệt so với struct { ... }. Cái trước là hợp lệ, nhưng cái sau thì không.
Johannes Schaub - litb

3
Vì C ++ nói chung khó phân tích cú pháp đến mức nào, tôi nghi ngờ tiêu chuẩn đã cam kết các cấu trúc và liên hiệp không được đặt tên không được phép chỉ để đơn giản hóa việc phân tích cú pháp.
Adrian McCarthy

@Adrian: Tôi nói C, không phải C ++. C ++ sử dụng cú pháp của C và mở rộng nó. Có lẽ những người tạo ra C ++ không thấy cần phải cho phép các thành viên struct / union không có tên để họ không làm rối phần đó của cú pháp.
kennytm

@Adrian, Tốt điểm có Adrian, tôi luôn không nghĩ "quá khó để thực hiện" bao giờ sẽ là một mối quan tâm của Bjarne và phi hành đoàn
bobobobo

C và C ++ đều hỗ trợ các công đoàn không tên, vì vậy nhận xét union { ... };không hợp lệ là không đúng.
bames53
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.