2013-02-27 29 views
5

Tôi có một enum như:C++ 11 loại để lập bản đồ enum?

enum E 
{ 
    TYPE_FLOAT, 
    TYPE_CHAR, 
    TYPE_INT 
} 

Và tôi muốn tạo một ánh xạ thời gian biên dịch để có được E thích hợp cho một loại như:

GetE<float> // returns TYPE_FLOAT 
GetE<char> // returns TYPE_CHAR 
GetE<int> // returns TYPE_INT 

tôi nghĩ:

template<class T> struct GetE; 

template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; }; 
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; }; 
template<> struct GetE<int> { static constexpr E type = TYPE_INT; }; 

Nhưng tôi gặp lỗi như:

undefined reference to `GetE<int>::type' 

Whats cách tốt nhất để làm điều này? Và tại sao lỗi?

Trả lời

6

Tùy thuộc vào cách bạn sử dụng các mã này biểu thức không đổi.

Các ODR (quy tắc một nét) cho rằng

(§3.2/2) [...] Một biến có tên xuất hiện như một biểu hiện khả năng đánh giá lại là ODR-sử dụng trừ khi nó là một đối tượng đáp ứng các yêu cầu xuất hiện trong biểu thức không đổi (5.19) và chuyển đổi từ rvalue sang rvalue (4.1) được áp dụng ngay lập tức. [...]

(Và sau đó, rất nhiều quy tắc đặc biệt, ngoại lệ và ngoại lệ của ngoại lệ theo.)

Bất kỳ biến mà ODR-sử dụng, phải có đúng một định nghĩa. Các biểu thức hằng số của bạn có một khai báo, nhưng không phải là một định nghĩa, vì vậy điều này diễn ra tốt trừ khi bạn sử dụng một trong số chúng.

Ví dụ, sau việc suôn sẻ:

int main() { 
    E e = GetE<float>::type; 
    return 0; 
} 

Nhưng điều này không:

void f(const E &) 
{ } 

int main() { 
    f(GetE<float>::type); 
    return 0; 
} 

f đòi hỏi một (const) tài liệu tham khảo, vì vậy việc chuyển đổi giá trị trái-to-rvalue không thể áp dụng ngay lập tức, do đó điều này tạo thành một sử dụng odr. Trình biên dịch sẽ phàn nàn rằng nó bỏ lỡ một định nghĩa.

(Lưu ý: Như ShafikYaghmour đã tìm thấy (xem nhận xét), bạn có thể không nhận được đơn khiếu nại nếu trình biên dịch sử dụng tối ưu hóa vì các tham chiếu có thể được tối ưu hóa. tùy thuộc vào trình biên dịch).)

Để giải quyết vấn đề, định nghĩa bắt buộc có thể được cung cấp theo cách thông thường, tức làngoài struct nét:

constexpr E GetE<float>::type; 
constexpr E GetE<char>::type; 
constexpr E GetE<int>::type; 

Nhưng vì đây sẽ phải xảy ra trong cpp (không phải là tập tin header), bạn sẽ kết thúc phải duy trì tờ khai và các định nghĩa ở hai nơi khác nhau, đó là rườm rà.

Các giải pháp bạn vừa đề xuất trong bình luận của bạn, ví dụ: xác định một constexpr (và inline) chức năng, âm thanh phải:

template <class T> constexpr E GetE(); 

template <> constexpr E GetE<float>() 
{ return TYPE_FLOAT; } 

template <> constexpr E GetE<char>() 
{ return TYPE_CHAR; } 

template <> constexpr E GetE<int>() 
{ return TYPE_INT; } 

void f(const E &) 
{ } 

int main() { 
    E e = GetE<float>(); 

    f(GetE<float>()); 

    return 0; 
} 
+1

Điều này nghe có vẻ hợp lý. Tôi đã thay đổi để sử dụng một mẫu chức năng như 'tmpl E GetE()' và sau đó chuyên thay thế và điều này đã sửa nó. Cảm ơn. –

+1

@jogojapan Tôi đang cố gắng tìm hiểu từ nhận xét của bạn nhưng tôi không thấy lỗi khi sử dụng đoạn mã cuối cùng: http://liveworkspace.org/code/4oTEis Tôi đang thiếu gì? Cảm ơn bạn –

+1

@ShafikYaghmour Đó là vì cờ trình biên dịch '-O2'. Nó tối ưu hóa các tham chiếu đi. Tốt bình luận, mặc dù, tôi sẽ đề cập đến điều này trong câu trả lời. – jogojapan

1

biến thành viên tĩnh cần phải định nghĩa ngoài phạm vi lớp:

class C { 
    const static int x = 5; 
}; 

decltype(C::x) C::x; 
+0

Thậm chí nếu nó 'constexpr'? Lạ nhỉ. –

+0

Vâng, trên GCC 4.7 nó hoạt động. Có lẽ nó chỉ là trình biên dịch của bạn? –

+0

sử dụng 4.7.2. –

1

Có lẽ vì bạn quên đặt một dấu chấm phẩy sau khi định nghĩa enum, điều này làm việc cho tôi trong LiveWorkSpace:

#include <iostream> 

enum E 
{ 
    TYPE_FLOAT, 
    TYPE_CHAR, 
    TYPE_INT 
} ; 

template<class T> struct GetE; 

template<> struct GetE<float> { static constexpr E type = TYPE_FLOAT; }; 
template<> struct GetE<char> { static constexpr E type = TYPE_CHAR; }; 
template<> struct GetE<int> { static constexpr E type = TYPE_INT; }; 

int main() 
{ 
    std::cout << GetE<int>::type << std::endl ; 
} 

đây là liên kết tới mã http://liveworkspace.org/code/nHqUe $ 6

+0

không có trong mã thực tôi có dấu chấm phẩy. Hmmm lạ. Tôi cũng đang sử dụng 4.7.2. Không chắc chắn những gì đang xảy ra. Cảm ơn. –

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