2014-04-11 13 views
20

liên quan: How to initialize a non-POD member in Unioncú đúp-hoặc-bằng-Initializer trong đoàn

Tiêu chuẩn nói

Tại nhiều nhất một thành viên dữ liệu không tĩnh của một liên minh có thể có một cú đúp-hoặc-bằng-initializer.

Nhưng

struct Point { 
    Point() {} 
    Point(int x, int y): x_(x), y_(y) {} 
    int x_, y_; 
}; 

union U { 
    int z; 
    double w; 
    Point p = Point(1,2); 
}; 


#include <iostream> 
int main() { 
    U u; 
    std::cout << u.p.x_ << ":" << u.p.y_ << std::endl; 
} 

in 4196960:0 thay vì dự kiến ​​1:2.

Tôi xem đây là lỗi trình biên dịch. Là vậy sao?

+1

Tôi không mong đợi điều này để biên dịch, vì hàm dựng mặc định của công đoàn sẽ bị xóa hoàn toàn do 'Điểm' không có hàm tạo mặc định tầm thường. Trừ khi, tất nhiên, việc khởi tạo thành viên đó được tính là một hàm tạo mặc định do người dùng cung cấp. – chris

+0

clang in ra 1: 2 –

+0

g ++ in 134514827: -1218232320 – Veritas

Trả lời

2

C++ 11 [class.ctor]/5 trạng thái:

Một mặc định constructor cho một lớp X là một constructor của lớp X có thể được gọi mà không cần một cuộc tranh cãi. Nếu không có constructor do người dùng khai báo cho lớp X, một hàm tạo không có tham số nào được khai báo ngầm định là mặc định (8.4). Một hàm tạo mặc định được khai báo ngầm là một thành viên inline public của lớp của nó. Một constructor mặc định mặc định cho lớp X được định nghĩa là bị xóa nếu:

  • X là một lớp đoàn giống như có một thành viên biến thể với một constructor mặc định không tầm thường,
  • bất kỳ thành viên dữ liệu không tĩnh với không brace-or-equal-initializer là loại tham chiếu,
  • bất kỳ thành phần dữ liệu không phải biến thể không thuộc loại đủ điều kiện (hoặc mảng của chúng) không có brace-or-equal-initializer không có một hàm tạo mặc định do người dùng cung cấp,
  • X là một công đoàn và tất cả các thành viên biến thể của nó là loại có đủ điều kiện (hoặc mảng của chúng),
  • X là một lớp phi công đoàn và tất cả thành viên của bất kỳ thành viên ẩn danh nào có loại của nó),
  • bất kỳ thành phần dữ liệu trực tiếp hoặc ảo hoặc thành viên dữ liệu không tĩnh nào không có bộ khởi tạo dấu ngoặc đơn hoặc bằng nhau, có loại lớp M (hoặc mảng của chúng) và M không có hàm tạo mặc định hoặc quá tải (13.3) như được áp dụng cho hàm xây dựng mặc định của M trong một sự mơ hồ hoặc trong một hàm bị xóa hoặc không thể truy cập được từ hàm tạo mặc định mặc định hoặc
  • bất kỳ lớp cơ sở trực tiếp hoặc ảo hoặc thành phần dữ liệu không tĩnh nào có loại có hàm hủy được xóa hoặc không truy cập được từ hàm tạo mặc định mặc định.

Một constructor mặc định là tầm thường nếu nó không phải là người dùng cung cấp và nếu:

  • lớp của nó không có chức năng ảo (10,3) và không có lớp cơ sở ảo (10.1), và
  • không có thành viên dữ liệu không tĩnh của lớp của nó có một cú đúp-hoặc-bằng-initializer, và
  • tất cả các lớp cơ sở trực tiếp của các lớp học có nhà thầu mặc định tầm thường, và
  • cho tất cả các thành phần dữ liệu không tĩnh của lớp của nó thuộc loại lớp (hoặc mảng của chúng), mỗi lớp như vậy có một hàm tạo mặc định tầm thường.

Nếu không, hàm tạo mặc định là không tầm thường.

Kể từ khi struct Point trong OP có một constructor mặc định không tầm thường,

Point() {} 

một constructor mặc định defaulted cho một sự kết hợp có chứa thành viên của kiểu Pointnên được định nghĩa là xóa theo viên đạn đầu tiên:

  • X là một liên minh giống như lớp có một thành viên biến thể với một nhà xây dựng mặc định không tầm thường

dẫn đến chương trình được trình bày trong OP bị hình thành.

Tuy nhiên, ủy ban dường như coi đây là một khiếm khuyết trong trường hợp một thành viên của một liên minh có một cú đúp-hoặc-bằng-initializer, mỗi core working group issue 1623:

Theo 12.1 [ class.ctor] đoạn 5,

Một mặc định constructor mặc định cho lớp X được định nghĩa là bị xóa nếu:

  • X là một lớp đoàn giống như có một thành viên biến thể với một constructor mặc định không tầm thường,

  • ...

  • X là một công đoàn và tất cả các thành viên biến thể của nó là const trình độ loại (hoặc mảng đó),

  • X là một lớp phi công đoàn và tất cả thành viên của bất kỳ thành viên công đoàn vô danh là loại const-đủ điều kiện (hoặc mảng đó),

  • ...

Bởi vì sự hiện diện của một initializer thành viên dữ liệu không tĩnh là tương đương với đạo đức của một mem-initializer, những quy tắc nên có lẽ được sửa đổi không xác định constructor tạo ra như bị xóa khi một thành viên công đoàn có bộ khởi tạo thành viên dữ liệu không tĩnh. (Lưu ý các tham chiếu không quy chuẩn trong 9.5 [class.union] đoạn 2-3 và 7.1.6.1 [dcl.type.cv] đoạn 2 cũng cần được cập nhật nếu giới hạn này bị thay đổi.)

Sẽ rất hữu ích khi thêm yêu cầu vào 9.5 [class.union] yêu cầu bộ khởi tạo thành viên dữ liệu không phải là tĩnh hoặc người dùng -trình xây dựng được hỗ trợ nếu tất cả các thành viên của công đoàn có các loại đủ điều kiện const.

Lưu ý chung hơn, tại sao hàm tạo mặc định được định nghĩa là đã xóa chỉ vì một thành viên có hàm tạo mặc định không tầm thường? Bản thân công đoàn không biết thành viên nào là thành viên tích cực và xây dựng mặc định sẽ không khởi tạo bất kỳ thành viên nào (giả sử không có trình khởi tạo dấu ngoặc đơn hoặc bằng nhau). Tùy thuộc vào "chủ sở hữu" của công đoàn để kiểm soát tuổi thọ của thành viên hoạt động (nếu có) và yêu cầu người xây dựng do người dùng cung cấp đang buộc một mẫu thiết kế không có ý nghĩa. Dọc theo cùng một dòng, tại sao destructor mặc định được định nghĩa là bị xóa chỉ vì một thành viên có một destructor không tầm thường? Tôi sẽ đồng ý với hạn chế này nếu nó chỉ áp dụng khi công đoàn cũng có một nhà xây dựng do người dùng cung cấp.

Issue 1623 có tình trạng "soạn thảo", chỉ ra rằng ủy ban cho rằng vấn đề này có lẽ là một khiếm khuyết - tại sao khác cho phép một cú đúp-hoặc-bằng-initializer cho một thành viên công đoàn? - nhưng chưa dành thời gian để xác định từ ngữ thích hợp cho một giải pháp. Thật vậy, đoạn này phần lớn giống nhau trong bản C++ 14 hiện tại N3936 ([class.ctor]/4), ngoại trừ từ ngữ "bất kỳ lớp cơ sở trực tiếp hoặc ảo hoặc thành phần dữ liệu không tĩnh" nào được thay thế bởi đơn giản hơn "bất kỳ subobject có khả năng xây dựng nào."

Mặc dù hành vi của cả hai trình biên dịch không tuân thủ nghiêm ngặt, tôi sẽ xem xét Clang hoạt động theo tinh thần của tiêu chuẩn. Nó sẽ xuất hiện mà GCC trở nên bối rối bởi sự kết hợp của constructor mặc định xóa và cú đúp-hoặc-bằng-initializer:

GCC có thể phải tuân thủ tiêu chuẩn và chẩn đoán chương trình là không đúng định dạng hoặc mô phỏng hành vi của vi khuẩn và tạo ra một hàm tạo thích hợp từ trình khởi tạo dấu ngoặc đơn hoặc bằng nhau.

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