Khởi tạo thành viên const trong khai báo lớp trong C ++


80

Trong PHP và C #, các hằng có thể được khởi tạo khi chúng được khai báo:

class Calendar3
{
    const int value1 = 12;
    const double value2 = 0.001;
}

Tôi có khai báo C ++ sau của một hàm được sử dụng với một lớp khác để so sánh hai vectơ toán học:

struct equal_vec
{
    bool operator() (const Vector3D& a, const Vector3D& b) const
    {
        Vector3D dist = b - a;
        return ( dist.length2() <= tolerance );
    }

    static const float tolerance = 0.001;
};

Mã này được biên dịch mà không gặp vấn đề với g ++. Bây giờ ở chế độ C ++ 0x (-std = c ++ 0x) trình biên dịch g ++ xuất ra thông báo lỗi:

error: 'constexpr' cần thiết để khởi tạo trong lớp của thành phần dữ liệu tĩnh 'dung sai' của kiểu không tích phân

Tôi biết tôi có thể định nghĩa và khởi tạo static constthành viên này bên ngoài định nghĩa lớp. Ngoài ra, một thành viên dữ liệu hằng số không tĩnh có thể được khởi tạo trong danh sách khởi tạo của một phương thức khởi tạo.

Nhưng có cách nào để khởi tạo một hằng trong khai báo lớp giống như nó có thể trong PHP hoặc C # không?

Cập nhật

Tôi đã sử dụng statictừ khóa chỉ vì có thể khởi tạo các hằng số như vậy trong khai báo lớp trong g ++. Tôi chỉ cần một cách để khởi tạo một hằng số trong một khai báo lớp bất kể nó có được khai báo là statichay không.


5
I used static keyword just because it was possible to initialize such constants within the class declaration in g++. I just need a way to initialize a constant in a class declaration no matter if it declared as static or not.Đó là cách sai lầm để quyết định một thành viên có nên statichay không. Đừng bao giờ để sự lười biếng từ vựng quyết định ngữ nghĩa của mã của bạn.
Các cuộc đua ánh sáng trong quỹ đạo

That's the wrong way to decide whether a member should be static or not.Tôi không đồng ý. Tôi nghĩ điều đó không thành vấn đề đối với các thành viên không đổi.
ezpresso 11/12/12

3
@expresso: Không hề. Bạn có thể khởi tạo một staticthành viên không cố định với thông tin về cá thể cụ thể. Việc bạn đã quyết định rằng hằng số của mình là một thuộc tính của kiểu chứ không phải của một trường hợp cụ thể là lý do để tạo ra nó static, không phải vì bạn tưởng tượng ra một phím tắt gõ.
Lightness Races in Orbit,

@lightless: Chà, có thể, nhưng tôi không thấy lý do gì để sử dụng việc khởi tạo các hằng số cụ thể giống nhau với các giá trị khác nhau. Tôi đã từng sử dụng các trường lớp không phải const cho điều đó!
ezpresso

4
Tại sao, nếu chúng không bao giờ thay đổi sau khi khởi tạo đối tượng? struct myType { const std::time_t instantiated; myType() : instantiated(std::time(0)) {} };Mọi thứ thể const nên được const; áp dụng cho staticvà không phải staticthành viên như nhau.
Các cuộc đua ánh sáng ở Orbit

Câu trả lời:


136

Trong C ++ 11, staticthành viên không phải dữ liệu, thành viên static constexprdữ liệu và static constthành viên dữ liệu của kiểu tích phân hoặc kiểu liệt kê có thể được khởi tạo trong khai báo lớp. ví dụ

struct X {
    int i=5;
    const float f=3.12f;
    static const int j=42;
    static constexpr float g=9.5f;
};

Trong trường hợp này, ithành viên của tất cả các thể hiện của lớp Xđược khởi tạo 5bởi phương thức khởi tạo do trình biên dịch tạo và fthành viên được khởi tạo thành 3.12. Thành static constviên dữ liệu jđược khởi tạo thành 42, và static constexprthành viên dữ liệu gđược khởi tạo thành 9.5.

floatdoublekhông thuộc kiểu tích phân hoặc kiểu liệt kê, các thành viên đó phải constexprhoặc không phải staticđể cho phép bộ khởi tạo trong định nghĩa lớp.

Trước C ++ 11, chỉ static constcác thành viên dữ liệu của kiểu tích phân hoặc kiểu liệt kê mới có thể có bộ khởi tạo trong định nghĩa lớp.


Có một liên kết đến một phần của tiêu chuẩn nơi điều này được chỉ định không? Tôi vừa mới bắt đầu sử dụng nó (mặc dù biên dịch dưới dạng C ++ 14) và rất vui vì nó hoạt động, vì tôi muốn tiết kiệm rất nhiều thời gian bằng cách nhờ trình biên dịch làm việc này cho tôi. Tuy nhiên, cho đến khi đọc câu trả lời của bạn, tôi không chắc liệu nó nên được làm việc! Một từ chính thức sẽ giúp tôi tự tin hơn, haha. Fwiw, tôi cũng có nó làm việc với các thành viên char const n[3]{'a', 'b', 'c'};.
underscore_d

Tôi tự hỏi tại sao static const intnhưng static constexpr float? nó có nghĩa là float and double are not of integral or enumeration type
mrgloom

@mrgloom: có, float và double thì không. Nếu kiểu dữ liệu có thể được dịch thành một số nguyên, thì đó là một kiểu tích phân.
ppadhy

45

Khởi tạo các biến thành viên tĩnh khác với kiểu const int không phải là chuẩn C ++ trước C ++ 11. Trình biên dịch gcc sẽ không cảnh báo bạn về điều này (và dù sao cũng tạo ra mã hữu ích) trừ khi bạn chỉ định -pedantictùy chọn. Sau đó, bạn sẽ gặp một lỗi tương tự như:

const.cpp:3:36: error: floating-point literal cannot appear in a constant-expression
const.cpp:3:36: warning: ISO C++ forbids initialization of member constant ‘tolerance’ of non-integral type ‘const float’ [-pedantic]

Lý do cho điều này là tiêu chuẩn C ++ không xác định cách thực hiện dấu phẩy động và được để cho bộ xử lý. Để giải quyết vấn đề này và những hạn chế khác constexprđã được giới thiệu.


14

Đúng. Chỉ cần thêm constexprtừ khóa như lỗi cho biết.


1
Có lẽ anh ấy không muốn yêu cầu C ++ 11 cho dự án của mình?
Marc Mutz - mmutz 14/02/12

6
Vấn đề ông đề cập đến đã được giới thiệu do thực tế ông đã cố gắng để biên dịch như C ++ 11, tức là ông muốn hỗ trợ C ++ 11 :)
Unknown1987

1

Nếu bạn chỉ cần nó trong một phương thức, bạn có thể khai báo nó tĩnh cục bộ:

struct equal_vec
{
    bool operator() (const Vector3D& a, const Vector3D& b) const
    {
        static const float tolerance = 0.001f;
        Vector3D dist = b - a;
        return ( dist.length2() <= tolerance );
    }
};

1

Tôi đã gặp phải vấn đề thực sự với điều này, bởi vì tôi cần cùng một mã để biên dịch với các phiên bản khác nhau của g ++ (trình biên dịch GNU C ++). Vì vậy, tôi phải sử dụng macro để xem phiên bản trình biên dịch nào đang được sử dụng và sau đó hành động tương ứng, như vậy

#if __GNUC__ > 5
 #define GNU_CONST_STATIC_FLOAT_DECLARATION constexpr
#else
 #define GNU_CONST_STATIC_FLOAT_DECLARATION const
#endif

GNU_CONST_STATIC_FLOAT_DECLARATION static double yugeNum=5.0;

Điều này sẽ sử dụng 'const' cho mọi thứ trước phiên bản g ++ 6.0.0 và sau đó sử dụng 'constexpr' cho g ++ phiên bản 6.0.0 trở lên. Đó là phỏng đoán về phiên bản mà thay đổi diễn ra, bởi vì thành thật mà nói tôi đã không nhận thấy điều này cho đến phiên bản g ++ 6.2.1. Để làm đúng, bạn có thể phải xem phiên bản nhỏ và số bản vá của g ++, vì vậy hãy xem

https://gcc.gnu.org/onlineocs/cpp/Common-Predefined-Macros.html

để biết chi tiết về các macro có sẵn.

Với gnu, bạn cũng có thể gắn bó với việc sử dụng 'const' ở mọi nơi và sau đó biên dịch với -fpermissivecờ, nhưng điều đó đưa ra cảnh báo và tôi thích nội dung của mình được biên dịch sạch sẽ.

Không tuyệt vời, vì nó dành riêng cho trình biên dịch gnu, nhưng tôi nghi ngờ bạn có thể làm tương tự với các trình biên dịch khác.

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.