2010-06-11 30 views
79

Sự hiểu biết của tôi là C++ cho phép các thành viên const tĩnh được định nghĩa bên trong một lớp miễn là nó là một kiểu số nguyên.Xác định các số nguyên const tĩnh trong định nghĩa lớp

Tại sao, sau đó, mã sau có cho tôi lỗi liên kết không?

#include <algorithm> 
#include <iostream> 

class test 
{ 
public: 
    static const int N = 10; 
}; 

int main() 
{ 
    std::cout << test::N << "\n"; 
    std::min(9, test::N); 
} 

Các lỗi tôi nhận được là:

test.cpp:(.text+0x130): undefined reference to `test::N' 
collect2: ld returned 1 exit status 

Điều thú vị là, nếu tôi nhận xét ra các cuộc gọi đến std :: phút, mã biên dịch và liên kết tốt (mặc dù thử nghiệm :: N cũng là được tham chiếu trên dòng trước đó).

Bất kỳ ý tưởng nào về những gì đang diễn ra?

Trình biên dịch của tôi là gcc 4.4 trên Linux.

+3

Hoạt động tốt trên Visual Studio 2010. – Puppy

+1

Lỗi chính xác này được giải thích tại https://gcc.gnu.org/wiki/VerboseDiagnostics#missing_static_const_definition –

+0

Trong trường hợp cụ thể của 'char', bạn có thể xác định nó thay vì' constexpr static const char & N = "n" [0]; '. Lưu ý '&'. Tôi đoán điều này hoạt động vì các chuỗi ký tự được xác định tự động. Tôi lo lắng về điều này mặc dù - nó có thể hành xử kỳ lạ trong một tập tin tiêu đề giữa các đơn vị dịch thuật khác nhau, như chuỗi có thể sẽ có nhiều địa chỉ khác nhau. –

Trả lời

49

Sự hiểu biết của tôi là C++ cho phép các thành viên const tĩnh được định nghĩa bên trong một lớp miễn là nó là một loại số nguyên.

Bạn sắp xếp chính xác. Bạn được phép khởi tạo các tích phân const tĩnh trong khai báo lớp nhưng đó không phải là một định nghĩa.

http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=/com.ibm.xlcpp8a.doc/language/ref/cplr038.htm

Điều thú vị là, nếu tôi nhận xét ra các cuộc gọi đến std :: phút, mã biên dịch và liên kết tốt (mặc dù thử nghiệm :: N cũng được giới thiệu trên các dòng trước).

Bất kỳ ý tưởng nào về những gì đang diễn ra?

std :: min lấy tham số của nó bằng tham chiếu const. Nếu nó lấy chúng theo giá trị bạn không có vấn đề này nhưng vì bạn cần một tham chiếu, bạn cũng cần một định nghĩa.

Đây là chương/bài kệ:

9.4.2/4 - Nếu một thành viên static dữ liệu là loại const không thể thiếu hoặc const liệt kê, khai báo trong định nghĩa lớp có thể chỉ định một không đổi initializer mà phải là một biểu thức hằng số không thể thiếu (5.19). Trong trường hợp đó, thành viên có thể xuất hiện trong các biểu thức không đổi. Thành viên vẫn sẽ được xác định trong phạm vi không gian tên nếu nó được sử dụng trong chương trình và định nghĩa phạm vi không gian tên sẽ không chứa một bộ khởi tạo .

Xem câu trả lời của Chu để giải quyết sự cố.

+0

Tôi hiểu, điều đó thật thú vị.Trong trường hợp đó, sự khác nhau giữa việc cung cấp giá trị tại điểm khai báo so với việc cung cấp giá trị tại điểm định nghĩa là gì? Bạn nên chọn loại nào? – HighCommander4

+0

Vâng, tôi tin rằng bạn có thể nhận được ngay mà không có một định nghĩa miễn là bạn không bao giờ thực sự "sử dụng" biến. Nếu bạn chỉ sử dụng nó như là một phần của một biểu thức không đổi thì biến đó sẽ không bao giờ được sử dụng. Nếu không, có vẻ như không có sự khác biệt lớn ngoài việc có thể thấy giá trị trong tiêu đề - có thể hoặc không thể là thứ bạn muốn. –

+1

Câu trả lời ngắn gọn là const const = 1; là một rvalue nhưng không phải là một lvalue. Giá trị có sẵn như là một hằng số tại thời gian biên dịch (bạn có thể kích thước một mảng với nó) static const y; [không khởi tạo] phải được định nghĩa trong một tệp cpp và có thể được sử dụng làm giá trị hoặc một giá trị. –

10

Không chỉ int's. Nhưng bạn không thể xác định giá trị trong khai báo lớp. Nếu bạn có:

class classname 
{ 
    public: 
     static int const N; 
} 

trong file .h sau đó bạn phải có:

int const classname::N = 10; 

trong file cpp.

+2

Tôi biết rằng bạn có thể * khai báo * một biến của bất kỳ loại nào bên trong khai báo lớp. Tôi nói rằng tôi nghĩ rằng hằng số nguyên tĩnh cũng có thể được * xác định * bên trong khai báo lớp. đây không phải là trường hợp? Nếu không, tại sao nó trình biên dịch không đưa ra một lỗi ở dòng mà tôi cố gắng định nghĩa nó bên trong lớp? Hơn nữa, tại sao std :: cout dòng không gây ra một lỗi linker, nhưng std :: min dòng nào? – HighCommander4

+0

Không, không thể xác định các thành viên tĩnh trong khai báo lớp vì khởi tạo phát ra mã. Không giống như một hàm nội tuyến cũng phát ra mã, định nghĩa tĩnh là duy nhất trên toàn cầu. –

+0

@ HighCommander4: Bạn có thể cung cấp bộ khởi tạo cho thành phần tách rời 'static const' trong định nghĩa lớp. Nhưng điều đó vẫn * không xác định * thành viên đó. Xem câu trả lời của Noah Roberts để biết chi tiết. – AnT

22

Một cách khác để làm điều này, với nhiều loại nguyên dù sao, là xác định các hằng số như enums trong lớp: Ví dụ

class test 
{ 
public: 
    enum { N = 10 }; 
}; 
+2

Và điều này có lẽ sẽ giải quyết được vấn đề. Khi N được sử dụng như một tham số cho min() nó sẽ gây ra một tạm thời được tạo ra thay vì cố gắng tham chiếu đến một biến được cho là tồn tại. –

37

Bjarne Stroustrup của in his C++ FAQ gợi ý bạn là chính xác, và chỉ cần một định nghĩa nếu bạn dành thời địa chỉ nhà.

class AE { 
    // ... 
public: 
    static const int c6 = 7; 
    static const int c7 = 31; 
}; 

const int AE::c7; // definition 

int f() 
{ 
    const int* p1 = &AE::c6; // error: c6 not an lvalue 
    const int* p2 = &AE::c7; // ok 
    // ... 
} 

Ông nói "Bạn có thể lấy địa chỉ của một thành viên tĩnh nếu (và chỉ nếu) nó có một định nghĩa out-of-class". Mà cho thấy nó sẽ làm việc khác. Có lẽ chức năng min của bạn gọi địa chỉ bằng cách nào đó đằng sau hậu trường.

+0

'std :: min' lấy tham số của nó bằng tham chiếu, đó là lý do tại sao một định nghĩa là bắt buộc. – Rakete1111

2

C++ cho phép các thành viên const tĩnh được định nghĩa bên trong một lớp

Nope, 3,1 §2 nói:

Một tuyên bố là một định nghĩa trừ khi nó tuyên bố một chức năng mà không chỉ định thân của hàm (8.4), nó chứa bộ định danh bên ngoài (7.1.1) hoặc một đặc tả liên kết (7.5) và không phải là bộ khởi tạo cũng như một hàm, nó khai báo một thành phần dữ liệu tĩnh trong định nghĩa lớp (9.4), nó là khai báo tên lớp (9.1), nó là khai báo đục (7.2), hoặc là khai báo kiểu gõ (7.1.3), khai báo sử dụng (7.3.3), hoặc chỉ thị sử dụng (7.3.4).

8

Dưới đây là một cách khác để làm việc xung quanh vấn đề: (. Tôi nghĩ rằng câu trả lời điên Eddie mô tả một cách chính xác tại sao những vấn đề tồn tại)

std::min(9, int(test::N)); 

+4

hoặc thậm chí 'std :: min (9, + test :: N); ' – Orient

+0

Đây là câu hỏi lớn: tất cả đều tối ưu? Tôi không biết về các bạn, nhưng sự thu hút lớn của tôi để bỏ qua định nghĩa là nó sẽ mất không có bộ nhớ và không có chi phí trong việc sử dụng const tĩnh. – Opux

2

Tính đến C++ 11 bạn có thể (và bạn muốn) để sử dụng:

static constexpr int N = 10;

mà không yêu cầu bạn xác định các khuyết điểm tant trong một tập tin .cpp.

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