2015-02-05 46 views
5

Đây là những gì tôi có bây giờ:Tuyên bố một thể hiện của một lớp bên trong lớp tuyên bố này và khởi tạo nó tại chỗ

class CColorf 
{ 
public: 
    CColorf(); 
    CColorf(float r, float g, float b, float a = 1.0f); 

public: 
    float r, g, b, a; 

// predefined colors 
    // rgb(0.0, 0.0, 1.0) 
    static const CColorf blue; 
}; 

Nó hoạt động với blue quy định tại ccolorf.cpp như vậy:

CColorf const CColorf::blue = CColorf(0.0f, 0.0f, 1.0f); 

Và đây là những gì tôi muốn làm:

class CColorf 
{ 
    ... 

// predefined colors 
    // rgb(0.0, 0.0, 1.0) 
    static const CColorf blue = CColorf(0.0f, 0.0f, 1.0f); 
}; 

Nhưng nó tạo ra một lỗi biên dịch:

a static data member with an in-class initializer must have non-volatile const integral type

Có cách nào để tránh sự cần thiết phải khai báo và định nghĩa riêng biệt tại đây không?

+2

Khai nó 'constexpr'. 'CColof' là một kiểu chữ, do đó nó sẽ hoạt động. – Columbo

+0

@Columbo: không phải 'constexpr' phải được sử dụng với các biểu thức (hoặc hàm), chứ không phải khai báo? –

+1

... không biết ý bạn là gì, nhưng bạn có vẻ bối rối. 'constexpr' là một trình khai báo khai báo (khai báo khai báo) và do đó chỉ có thể được sử dụng trong các khai báo. – Columbo

Trả lời

1

Bạn không thể làm điều đó.

Thông báo lỗi ngụ ý rằng bạn đang biên dịch thành C++ 03, trong đó chỉ các thành viên tĩnh không đổi của loại tích phân có thể được khởi tạo trong khai báo của chúng; vì vậy bạn không thể làm điều này cho bất kỳ loại lớp nào.

C++ 11 thư giãn các quy tắc, nhưng vẫn có những hạn chế:

  • loại phải đen. Bạn có thể làm cho loại chữ này bằng cách tạo các hàm tạo constexpr; nhưng
  • loại phải hoàn, và một lớp không được hoàn thành trong vòng định nghĩa của nó (trừ định nghĩa thành viên bên trong)
  • thành viên phải không ODR-sử dụng; nghĩa là, bạn chỉ có thể sử dụng nó như là một biểu thức rvalue và không thể lấy địa chỉ của nó hoặc tạo tham chiếu đến nó.

Trong khi điểm đầu tiên có thể được sửa, và thứ ba sẽ hạn chế những gì bạn có thể làm với thành viên, không cho dù bạn có thể xác định nó, thứ hai làm cho nó không thể. Bạn sẽ phải xác định biến theo cách thông thường, bên ngoài lớp trong một đơn vị dịch.

Nếu bạn muốn giữ lại tất cả mọi thứ trong định nghĩa lớp, và giá trị có sẵn để giúp tối ưu hóa thời gian biên dịch, bạn có thể định nghĩa một hàm chứ không phải là một biến

static CColorf blue() {return CColorf(0.0f, 0.0f, 1.0f);} 
3

Quy tắc chung ở đây là bạn không thể sử dụng khởi tạo thành viên trong lớp của biến thành viên nếu đó là static (và cũng không phải const int), tuy nhiên có một số ngoại lệ (không có trường hợp nào áp dụng cho trường hợp của bạn).

Trong tiêu chuẩn C++ 98, bạn có thể chỉ thành viên khởi static const int

Trong tiêu chuẩn 11 C++, bạn có thể khởi tạo thành viên tất cả mọi thứ trừ static (với ngoại lệ đối với các tiêu chuẩn C++ 98).

Bạn có thể làm được việc này nếu thành viên tĩnh của bạn là constexpr:

§ 9.4.2 (tháng 11 năm 2014 dự thảo)

If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression (5.20). A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. — end note ] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer.

Để giải thích đoạn này một chút rõ ràng hơn: Nếu bạn muốn cố gắng thu hút mọi thứ với constexpr, loại của bạn phải là "theo nghĩa đen".

Một loại đen (§ 3.9.10):

  • Có một "tầm thường" destructor
  • Có chỉ nhà xây dựng biểu thức hằng
  • Có chỉ lớp đen kiểu cơ sở dữ liệu và các thành viên
  • Hoặc là một aggregate type
  • Hoặc là void, vô hướng (ví dụ: int), tham chiếu hoặc mảng loại chữ

Một destructor là "tầm thường" nếu:

  • Đó là trình biên dịch tạo ra (ví dụ: bạn không xác định một)
  • Và mỗi đối tượng thành viên không tĩnh có một destructor tầm thường

Với tất cả những điều này, bạn có thể có một cái nhìn tại mã của bạn và nghĩ rằng "Hm, tôi cũng có thể làm cho tất cả các nhà thầu của tôi constexpr và sau đó thay đổi static const CColorf blue thành static constexpr CColorf blue và tôi rất tốt. "

Tuy nhiên, lớp học của bạn "chưa hoàn thành" tại thời điểm bạn khai báo tĩnh.Hãy suy nghĩ về những điều sau đây example:

class A{ 
    private: 
     A member; 
} 

Mỗi thể hiện của A hiện nay có một thể hiện của A. Trình biên dịch phân bổ bao nhiêu byte cho A? Nó không thể nói được. Vô cùng nhiều, có lẽ, do đệ quy. A là không đầy đủ bên trong lớp riêng của nó. Bạn có vấn đề tương tự về sự không hoàn thiện. Tuy nhiên, chúng ta hãy làm cho nó một con trỏ thay vì:

class A{ 
    private: 
     A* member; 
} 

Bây giờ thật dễ dàng vì A* là một loại con trỏ, mà trình biên dịch biết kích thước của.

Vì vậy, bây giờ bạn nghĩ "Được rồi, tôi sẽ chỉ làm cho static constexpr CColorf blue một con trỏ như static constexpr CColorf* blue = new CColorf(0.0f, 0.0f, 1.0f);

Nhưng bạn có thể không, bởi vì các nhà điều hành new không phải là constexpr.

Và bạn không thể thử const bởi vì chúng tôi đã đi qua tại sao.

vì vậy, có lẽ bạn nghĩ về quá tải toán tử newconstexpr, nhưng you can't do that either.

Vì vậy, bạn đã hết may mắn.

+0

Xem nhận xét của Mike Seymour về câu hỏi. Những gì bạn đang đề xuất là _not_ một câu trả lời. –

+0

Câu trả lời cho câu hỏi của tôi là một đoạn mã chỉ cách làm những gì tôi muốn, hoặc 3 từ: "Điều này là không thể". "Câu trả lời" của bạn không phải là; nó nói "bạn có thể tránh được điều này nếu thành viên tĩnh của bạn là' constexpr' ", nhưng làm cho các thành viên tĩnh của tôi' constexpr', trên thực tế, không giải quyết được vấn đề. Chưa kể rằng đoạn văn bản không thể đọc được của con người từ tiêu chuẩn hầu như chẳng nói gì cho tôi cả. –

+0

@VioletGiraffe: Tôi xin lỗi. Đôi khi mọi thứ rõ ràng với một người có thể không rõ ràng với người khác. Tôi đã thêm nhiều chi tiết hơn vào câu trả lời của mình, điều mà tôi hy vọng làm cho mọi việc trở nên rõ ràng hơn. – AndyG

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