2016-03-10 15 views
8

Tôi biết có rất nhiều câu hỏi tương tự, nhưng bằng một số câu hỏi khác nhau. Đó là về tình huống sau:Chỉ định thành viên lớp constexpr tĩnh cho biến thời gian chạy

#include <iostream> 
#include <array> 

template<typename T> class MyClass 
{ 
public: 
    static constexpr std::array<T,4> ARRAY {{4, 3, 1, 5}}; 
}; 

int main() 
{ 
    constexpr std::array<int, 4> my_array(MyClass<int>::ARRAY); // works fine -> can use the ARRAY to initialize constexpr std::array 

    constexpr int VALUE = 5*MyClass<int>::ARRAY[0]; // works also fine 

    int value; 
    value = my_array[0]; // can assign from constexpr 
    value = MyClass<int>::ARRAY[0]; // undefined reference to `MyClass<int>::ARRAY 

    std::cout << VALUE << std::endl; 
    std::cout << value << std::endl; 

    return 0; 
} 

Theo như tôi hiểu constexpr là các hằng số biên dịch. Vì vậy, trình biên dịch có thể thực hiện một số phép tính, ví dụ để tính toán VALUE. Ngoài ra tôi rõ ràng có thể xác định một constexpr std::array<,>, từ đó tôi có thể gán các giá trị cho các biến thời gian chạy. Tôi hy vọng trình biên dịch sẽ đặt value = 4 vào chương trình thực thi, để tránh hoạt động tải. Tuy nhiên, tôi không thể gán trực tiếp từ các thành viên tĩnh, nhận được lỗi

undefined reference to `MyClass<int>::ARRAY' 
clang-3.7: error: linker command failed with exit code 1 

mà làm cho không có ý nghĩa với tôi, bởi vì nó có thể được thực hiện với một bước trung gian của một biến khác constexpr.

Vì vậy, câu hỏi của tôi là: Tại sao một thành viên constexpr tĩnh của một lớp không được gán cho một biến thời gian chạy?

Lưu ý: Trong MWE của tôi, lớp là lớp mẫu, không ảnh hưởng đến lỗi. Tuy nhiên, ban đầu tôi quan tâm đến trường hợp cụ thể này, điều mà tôi mong đợi là tổng quát hơn đối với một lớp không phải mẫu.

(Compiler là clang++ hoặc g++ với -std=c++11 - họ cung cấp cho các lỗi tương tự)

Edit: @Bryan Chen: Quên các dòng đầu ra. Được thêm ngay bây giờ.

+0

'clang ++' có vấn đề này: http://coliru.stacked-crooked.com/a/e9698f2bb249e509. Nhưng 'g ++' hoạt động: http://coliru.stacked-crooked.com/a/5ef23fe29b0aaa28. Lỗi Clang? –

+0

VS2015 nói điều này: 'khởi tạo trong lớp cho loại 'const std :: mảng ' chưa được triển khai; thành viên tĩnh sẽ vẫn chưa được khởi tạo trong thời gian chạy nhưng sử dụng trong các biểu thức hằng số được hỗ trợ'. Bạn có thể đang chạy vào một cái gì đó tương tự. –

+0

@Bryan: Tôi đã không chắc chắn. Tôi đã cài đặt g ++ 4.8.5, chưa hỗ trợ C++ 14. Nhưng tôi mong đợi điều này để làm việc đã cho C++ 11 !? – marlam

Trả lời

7

Các undefined reference là một lỗi mối liên kết. Quy tắc là nếu một biến là odr-sử dụng thì nó phải có định nghĩa. Điều này áp dụng ngay cả đối với các biến số constexpr.

Giống như hầu hết các quy tắc ODR, vi phạm đó là hành vi không xác định mà không cần chẩn đoán (có thể giải thích tại sao bạn không thấy chẩn đoán đối với một số cách sử dụng giá trị của bạn).

Để khắc phục lỗi này, thêm một định nghĩa bên ngoài lớp:

template<typename T> constexpr std::array<T,4> MyClass<T>::ARRAY; 

Vì nó là một mẫu bạn thực sự có thể đặt điều này trong tiêu đề, như trái ngược với các trường hợp thông thường mà định nghĩa đi trong đúng một .cpp tệp.


Vấn đề chính ở đây là liệu ARRAY[0] đếm như ODR sử dụng.Theo this detailed post, trong C++ 11 và C++ 14, việc lập chỉ mục một mảng được tính là odr-use, nhưng điều này đã bị thay đổi bởi DR 1926 được gửi chống lại C++ 14 không phải là odr-use.

Tuy nhiên, đó là nói về mảng dựng sẵn. IDK cho dù lý do tương tự áp dụng cho std::array, tôi tìm thấy văn bản của [basic.def.odr]/3 khó hiểu. Theo số informal definition on cppreference, std::array::operator[] sẽ gây ra odr-use của mảng vì giá trị trả về của nó liên kết tham chiếu đến mảng.

+0

Thx cho câu trả lời hay. Tôi có hiểu đúng không, việc sử dụng tại thời gian biên dịch cho các đối tượng constexpr khác là _not_ odr-use và do đó không cần định nghĩa? Nói cách khác: Trình biên dịch có thể tính toán 'VALUE' bằng cách sử dụng toán tử'] vì nó không cần tham chiếu đến (và do đó không có định nghĩa), nhưng tại thời gian chạy 'giá trị' không thể tính được vì địa chỉ là cần thiết và định nghĩa là gì? – marlam

+0

@marlam Tôi nghĩ rằng chúng đều là sử dụng không đúng, nhưng nó chỉ là một số chi tiết của trình biên dịch mà tôi không biết về điều đó làm cho nó cho lỗi trong một trường hợp nhưng không phải là khác. –

4

Vì lý do này, tôi luôn trả về các đối tượng constexpr từ một hàm constexpr.

Mã được sửa đổi bên dưới. Lưu ý rằng do thiếu C++ 14 trong std::array<>, bạn phải trả lại const std::array để cho phép operator[] hoạt động.

#include <iostream> 

#include <iostream> 
#include <array> 

template<typename T> class MyClass 
{ 
public: 
    static constexpr const std::array<T,4> ARRAY() { return {4, 3, 1, 5}; }; 
}; 

int main() 
{ 
    constexpr std::array<int, 4> my_array(MyClass<int>::ARRAY()); // works fine -> can use the ARRAY to initialize constexpr std::array 

    constexpr int VALUE = 5 * MyClass<int>::ARRAY()[0]; // works also fine 

    int value; 
    value = my_array[0]; // can assign from constexpr 
    value = MyClass<int>::ARRAY()[0]; // undefined reference to `MyClass<int>::ARRAY 

    std::cout << VALUE << std::endl; 
    std::cout << value << std::endl; 

    return 0; 
} 

kết quả mong đợi:

20 
4 
+0

Thx! Tôi có hiểu đúng không, toán tử chỉ số sẽ làm việc cho các phép tính thời gian biên dịch chứ không phải trong thời gian chạy? Và đây có phải là sự thiếu hụt đặc biệt của 'std :: array' hay tình huống này xuất hiện thường xuyên hơn (như bạn nói, bạn cũng trả về các đối tượng constexpr từ hàm constexpr). Và điều này có nhiều khả năng được sửa chữa hoặc có lý do để trở thành như vậy không? Và cuối cùng: Không phải là một lỗ hổng thiết kế để có được mảng thông qua một chức năng? – marlam

+0

@marlam nó đang được cố định trong C++ 17. Đó là vì std :: array's operator [] không phải là constexpr trong trường hợp có thể thay đổi được (giao diện được định nghĩa trong C++ 11 và họ quên cập nhật nó trong C++ 14 khi các đối tượng constexpr có thể thay đổi được). Vì vậy, toán tử subscript hiện sẽ làm việc trong mọi trường hợp cho một mảng không phải là constexpr và sẽ làm việc cho một mảng constexpr const. Sự thiếu hụt này không phổ biến và những người viết các đối tượng constexpr như C++ 14 (như tôi) nhận thức được rằng chúng có thể biến đổi được –

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