2012-06-06 32 views
6

Mã dưới đây thể hiện hành vi của gcc 4.6.2 mà tôi không thể giải thích. Hàm đầu tiên khai báo một mảng tĩnh kiểu vec_t, trong đó vec_t là một bí danh typedef'd cho unsigned char. Hàm thứ hai giống hệt nhau, ngoại trừ loại vect_t là một tham số mẫu. Lỗi thứ hai không thể biên dịch với chẩn đoán ": kích thước bộ nhớ của‘ bitVec ’không cố định".Lỗi trình biên dịch? g ++ cho phép các mảng tĩnh có kích thước thay đổi, trừ khi hàm được templated

#include <limits> 

void bitvec_func() 
{ 
    const std::size_t  nbits = 1e7; 
    typedef unsigned char vec_t; 
    const std::size_t  WLEN = std::numeric_limits<vec_t>::digits; 
    const std::size_t  VSIZ = nbits/WLEN+1; 
    static vec_t   bitVec[nbits/VSIZ]; // Compiles fine 
} 

template <typename T> 
void bitvec_func() 
{ 
    const std::size_t  nbits = 1e7; 
    typedef T    vec_t; 
    const std::size_t  WLEN = std::numeric_limits<vec_t>::digits; 
    const std::size_t  VSIZ = nbits/WLEN+1; 
    static vec_t   bitVec[nbits/VSIZ]; // "error: storage size of ‘bitVec’ isn’t constant" 
} 

void flarf() 
{ 
    bitvec_func(); 
    bitvec_func<unsigned char>(); 
} 

Dường như với tôi rằng việc tạo mẫu với đối số < unsigned char> sẽ khiến trình biên dịch tạo mã giống như hàm đầu tiên. Bất cứ ai có thể cung cấp bất kỳ cái nhìn sâu sắc vào lý do tại sao điều này dường như không phải là trường hợp?

[Phụ lục: hàm thứ hai sẽ biên dịch với "-std = C++ 0x" hoặc "-std = gnu ++ 0x", nhưng tôi vẫn muốn hiểu cách/nếu nó sai theo định nghĩa ngôn ngữ trước đó]

ETA:.
chức năng thứ hai sẽ biên dịch nếu initializer cho nbits được thay đổi:

const std::size_t  nbits = 1e7;    // Error 
const std::size_t  nbits = (std::size_t)1e7; // Okay 
const std::size_t  nbits = 10000000.0;  // Error 
const std::size_t  nbits = 10000000;   // Okay 

Nói cách khác, có vẻ như rằng nếu nbits được khởi tạo với một biểu hiện của một thể thiếu loại, sau đó nbits được coi là liên tục trong định nghĩa của bitVec. Nếu nbits thay vào đó được khởi tạo với biểu thức dấu phẩy động, trình biên dịch sẽ không còn nhìn thấy nó dưới dạng hằng số trong biểu thức cho thứ nguyên của bitVec và quá trình biên dịch không thành công.

Tôi gọi rất ít "lỗi trình biên dịch" trong C++ hơn tôi sẽ ở trong C, nhưng tôi không thể nghĩ ra bất kỳ lý do nào khác khiến 4 trường hợp trên không giống hệt về mặt ngữ nghĩa. Bất cứ ai khác quan tâm đến opine?

+0

Bạn có thể đăng mã chính xác cho bạn lỗi trình biên dịch không? Tôi không thể tái tạo nó. –

+0

Đó là mã chính xác ở trên. Trình biên dịch là gcc 4.6.2 và các tùy chọn là "-O0 -g3 -c". –

+1

Trên gcc cũ hơn 4.3.4, mã này [biên dịch tốt] (http://ideone.com/65Cl7). –

Trả lời

6

Sau khi biên dịch mã của bạn với -ansi trên gcc 4.7.0, tôi đã có thể để tái tạo cảnh báo này:

warning: ISO C++ forbids variable length array 'bitVec' [-Wvla] 

Cảnh báo này xuất hiện cho cảbitVec, không chỉ là một trong các mẫu chức năng. Sau đó tôi nhận ra rằng dòng nbits = 1e7; đang chỉ định một double cho một số unsigned int. Tôi nghĩ vì lý do này, vì một lý do nào đó gây ra nbits không phải là một biểu thức liên tục. Lý do mã của bạn được biên dịch cho phiên bản không được tạo mẫu là do phần mở rộng mảng biến chiều dài cho gcc. Ngoài ra, phiên bản gcc của bạn vì một số lý do không cho phép các mảng độ dài biến đổi trong các mẫu chức năng. Để sửa đổi thay đổi mã của bạn 1e7; thành 10000000.

EDIT

Tôi hỏi another question liên quan đến quy tắc. Câu trả lời là trong C++ 03 mã không hợp lệ, nhưng trong C++ 11 thì không sao.

+0

nó không làm mất thuộc tính' const', nhưng thuộc tính 'constexpr'' (ngụ ý). –

+0

@MooingDuck: Đúng vậy (đã sửa). Tuy nhiên, tôi vẫn tự hỏi tại sao lại như thế. –

+0

'1e7' và' 0x1e7' là các số rất khác nhau. '10000000' sẽ là một sự thay thế tốt hơn. – aschepler

0

Chắc chắn câu trả lời là ở giai đoạn biên dịch mà lỗi đó được tạo ra, kích thước lưu trữ của chỉ mục mảng không phải là hằng số - tức là nó ở phía trên của mở rộng mẫu. Mảng động không phải là một phần của C++ 98/03, chúng là phần mở rộng gcc (đến C, ban đầu). Vì vậy, lỗi là trong thực tế chính xác, ngay cả khi việc thực hiện có vẻ lạ. Có lẽ GCC đạt được một đường dẫn tuân thủ tiêu chuẩn cho các mảng khuôn mẫu, hỗ trợ chúng khi được yêu cầu và đưa ra một lỗi khác, nhưng các mảng kiểu tĩnh nhấn vào đường dẫn "C" và do đó tự động nhận phần mở rộng gcc.

+1

Mã không thực sự có mảng biến, do đó, lỗi là _wrong_. Kích thước của mảng được biết tại thời gian biên dịch, và cần được xử lý như vậy bởi trình biên dịch phù hợp. –

+0

Nó không chỉ đơn giản là một vấn đề về biến đổi thời gian chạy. Tôi tin (nhưng quá lười để kiểm tra), C++ 03 không cho phép tham số mẫu cụ thể trong bộ khởi tạo kích thước mảng (chính xác là do vấn đề gà và trứng với việc mở rộng chúng vào đúng thời điểm). Không nghi ngờ ai đó với kiến ​​thức tốt hơn về spec sẽ đến cùng để sửa tôi. –

+0

Thú vị. Tác vụ (mess) này hoạt động: 'template void a() { typedef T \t \t vec_t; tĩnh vec_t \t bitVec [sizeof (vec_t) * 8]; } ' –

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