2009-12-08 18 views
5

Tôi đang viết C++ bằng trình biên dịch MinGW GNU và sự cố xảy ra khi tôi cố gắng sử dụng biến số nguyên được xác định bên ngoài như một trường hợp trong câu lệnh chuyển đổi. Tôi nhận được lỗi trình biên dịch sau đây: "nhãn trường hợp không giảm xuống hằng số nguyên".C++ Switch sẽ không biên dịch với biến được xác định bên ngoài được sử dụng như trường hợp

Bởi vì tôi đã xác định biến số nguyên như extern Tôi tin rằng nó nên biên dịch, không ai biết vấn đề có thể là gì?

Dưới đây là một ví dụ:

test.cpp

#include <iostream> 
#include "x_def.h" 

int main() 
{ 
    std::cout << "Main Entered" << std::endl; 


    switch(0) 
    { 
     case test_int: 
     std::cout << "Case X" << std::endl; 
     break; 
     default: 
     std::cout << "Case Default" << std::endl; 
     break; 
    } 

    return 0; 
} 

x_def.h

extern const int test_int; 

x_def.cpp

const int test_int = 0; 

Mã này sẽ biên dịch một cách chính xác trên Visual C++ 2008. Tiếp tục Một người bạn Montanan của tôi đã kiểm tra tiêu chuẩn ISO C++ và nó xuất hiện rằng bất kỳ biểu thức const-integer nào cũng hoạt động. Đây có phải là lỗi trình biên dịch hay tôi đã bỏ sót điều gì đó hiển nhiên?

Dưới đây là thông tin phiên bản trình biên dịch của tôi:

Reading specs from C:/MinGW/bin/../lib/gcc/mingw32/3.4.5/specs
Configured with: ../gcc-3.4.5-20060117-3/configure --with-gcc --with-gnu-ld --with-gnu-as --host=mingw32 --target=mingw32 --prefix=/mingw --enable-threads --disable-nls --enable-languages=c,c++,f77,ada,objc,java --disable-win32-registry --disable-shared --enable-sjlj-exceptions --enable-libgcj --disable-java-awt --without-x --enable-java-gc=boehm --disable-libgcj-debug --enable-interpreter --enable-hash-synchronization --enable-libstdcxx-debug
Thread model: win32
gcc version 3.4.5 (mingw-vista special r3)

+2

Thú vị: một thực dụng và hai câu trả lời dựa trên tiêu chuẩn, tất cả từ những người có> đại diện 5K và câu trả lời dựa trên tiêu chuẩn không đồng ý. Methinks tiêu chuẩn không rõ ràng như nó có thể được. –

+0

@David Thronley: Đó là niềm vui của tiêu chuẩn :). –

+2

Tôi đang cố gắng để xác định sự liên quan của bạn của bạn là từ Montana. Montanans có nổi tiếng về việc giải thích các tiêu chuẩn không? –

Trả lời

5

Nhãn case yêu cầu biểu thức hằng số không thể tách rời có yêu cầu nghiêm ngặt cho phép xác định giá trị của chúng tại thời điểm biên dịch tại thời điểm sử dụng.

Từ 5,19 [expr.const], "một không thể thiếu biểu thức hằng có thể liên quan đến chỉ literals (2.13), điều tra viên, các biến const hay các thành viên dữ liệu tĩnh của loại tách rời hoặc liệt kê khởi tạo với các biểu thức hằng số (8,5), ... ".

Tại điểm mà tại đó bạn sử dụng test_int nơi một biểu thức hằng là cần thiết, nó là một biến const tuyên bố extern và không có bất kỳ initializer và không đáp ứng các yêu cầu cho một biểu thức hằng số, bất chấp thực tế rằng bạn không thực sự khởi tạo nó với một biểu thức liên tục tích phân trong một đơn vị dịch khác. (* Đây không phải là hoàn toàn rõ ràng từ ngữ của tiêu chuẩn nhưng là giải thích hiện tại của tôi của nó.)

Những hạn chế trong tiêu chuẩn không cho phép tập quán như:

void f(int a, int b) 
{ 
    const int c = b; 

    switch (a) 
    { 
    case c: 
     //... 
    } 
} 

Trong ví dụ của bạn, khi trình biên dịch đang biên soạn test.cpp, không có cách nào để xác định trình khởi tạo có thể là gì trong x_def.cpp. Bạn có thể thực hiện:

const int test_int = (int)time(); 

Rõ ràng, trong không ai trong số những ví dụ có thể giá trị của const int được xác định tại thời gian biên dịch mà là ý định cho các biểu thức hằng số tích phân.

+1

Tôi dám nói rằng nó vẫn là một khiếm khuyết trong tiêu chuẩn. Văn bản bạn tham chiếu không nói bất cứ điều gì về "cùng một đơn vị dịch". Nó nói "biến const" - kiểm tra - "khởi tạo với các biểu thức liên tục" - kiểm tra. –

+0

Tôi đồng ý rằng nó không rõ ràng như nó có thể được, nhưng tôi vẫn sẽ bảo vệ giải thích của tôi. Nếu chúng ta quên đơn vị dịch thuật khác và chỉ xem xét test.cpp: 'extern const int test_int;' là test_int một biến const được khởi tạo với một biểu thức không đổi? Tôi xin nói rằng việc thực hiện có quyền nói không vào thời điểm này và từ chối bản dịch. Phần 2.1 nói rằng các đơn vị dịch thuật được phân tích ngữ nghĩa trước khi các đơn vị dịch thuật được kết hợp và biểu thức liên tục dường như là một hạn chế ngữ nghĩa cần được áp dụng trong bước 7 của các giai đoạn dịch thuật. –

+0

@Charles: Ví dụ của bạn hơi khác một chút. Tiêu chuẩn là rõ ràng rằng yêu cầu không chỉ cho một biến 'const', mà biến' const' được khởi tạo với một biểu thức liên tục - và tham số của bạn 'int b' rõ ràng là * không * một biểu thức hằng, vì vậy' c' rõ ràng không phải là một biểu thức liên tục. –

4

nhãn Trường hợp phải lập thời gian hằng số. Điều đó có nghĩa là trình biên dịch phải có khả năng thay thế giá trị tại thời gian biên dịch. Mặc dù giá trị của bạn là không đổi, trình biên dịch không thể biết giá trị của chúng cho đến ít nhất là thời gian liên kết.

3

VC++ là đúng và g ++ sai. Một nhãn trường hợp được yêu cầu là một integral constant expression (§6.4.2/2) và một biến const của kiểu số nguyên được khởi tạo với một biểu thức không đổi là một biểu thức không đổi (§5.19/1).

Chỉnh sửa: chủ yếu cho Pavel và đề xuất của ông về DR có thể. §5.19/2 đã được hoàn toàn được viết lại rồi. C++ 0x thêm một khái niệm hoàn toàn mới về một số constexprmở rộng những gì được coi là một biểu thức liên tục đáng kể. Ví dụ: theo tiêu chuẩn hiện tại, chẳng hạn như:

int x() { return 10; } 
const int y = x(); 

y không phải là biểu thức liên tục. Tất cả chúng ta có thể dễ dàng thấy rằng nó (gián tiếp) được khởi tạo với một chữ số 10, nhưng trình biên dịch vẫn không thể cho phép nó như là một biểu thức liên tục. Theo tiêu chuẩn mới, có thể chỉ định x() làm constexpry sẽ là biểu thức không đổi.

Khi được xây dựng trong N2960, §5.19/2 cho biết một biểu thức là một biểu thức không đổi trừ khi nó sử dụng một cái gì đó từ danh sách sau đây. Sau đó nó đưa ra một danh sách dài trang, nhưng sử dụng biến số const không được khởi tạo trong đơn vị biên dịch hiện tại dường như không phải là một trong số chúng. [Chỉnh sửa: xem dưới đây - đọc CWG Số 721, tôi đã thay đổi quyết định của tôi.]

Theo như VC++ đúng và sai g ++, tôi chỉ có ý nghĩa này. Không có câu hỏi rằng cả hai là "sai" nếu bạn đang nói về việc nhận được tất cả các phần của tiêu chuẩn chính xác. Tôi nghi ngờ bất cứ ai thậm chí còn làm việc về việc triển khai export cho cả hai.Tuy nhiên,

export sẽ chỉ ra mức độ C++ có vẻ sẵn sàng hoãn các quyết định cho đến khi thời gian liên kết. Tra cứu tên hai giai đoạn có nghĩa là khi một mẫu được xuất được biên dịch, có nhiều hơn chỉ là các biểu thức liên tục mà nó không biết chắc chắn. Nó thậm chí có thể không biết một tên cụ thể đề cập đến một chức năng hoặc một đối tượng - nhưng không có câu hỏi rằng tiêu chuẩn không yêu cầu chính xác điều đó. Vấn đề đang xảy ra với tôi là đáng kể là đơn giản hơn để giải quyết.

Chỉnh sửa: Tôi đã tìm kiếm một chút và tìm thấy Vấn đề của Nhóm làm việc cốt lõi 721. Câu hỏi của Jame tương tự với câu hỏi gần đây ("Tuy nhiên, điều này không cần thiết, vì có lẽ cùng một đơn vị dịch và đứng trước biểu thức liên tục ... "). Độ phân giải được đề xuất thêm cụm từ: "... với khởi tạo trước ...". Ít nhất khi tôi đọc nó, điều đó có nghĩa là ủy ban đã đồng ý rằng theo tiêu chuẩn hiện hành, mã phải được chấp nhận, nhưng theo tiêu chuẩn mới thì nó không được phép.

Từ ngữ đã được thỏa thuận vào tháng 7 năm nay, nhưng không (chưa?) Xuất hiện trong N2960, mà tôi tin là bản nháp mới nhất của C++ 0x.

+6

Tôi đã lấy 5,19/1 để có nghĩa là khởi tạo với một biểu thức liên tục * trong đơn vị dịch * này. OK, trong một số đơn vị dịch thuật khác chưa được viết, có thể nó được khởi tạo với một biểu thức liên tục. Hoặc với một biểu thức không liên tục. Trình biên dịch không có cách nào để biết. Tôi thành thật không nghĩ rằng ý định của tiêu chuẩn là trì hoãn cho đến khi liên kết thời gian tạo ra các câu lệnh switch (và tất cả các mã khác sử dụng các biểu thức không đổi, ví dụ kích thước mảng và kích thước của các trường bit). –

+0

Hoặc nhìn vào nó theo một cách khác: trong ngữ cảnh biên dịch test.cpp, test_int không được khởi tạo với một biểu thức liên tục. Nó không được khởi tạo, nó chỉ được khai báo bên ngoài. Tôi không nghĩ rằng 'sizeof' có thể làm việc, nếu nó không thể biết kích thước của một thành viên mảng của một cấu trúc cho đến khi một số dll được nạp có chứa khởi tạo của một' extern const int'. –

+0

@Steve, không có gì trong văn bản của Tiêu chuẩn để hỗ trợ giải thích này. Tôi cũng nghĩ rằng đó là những gì có thể dự định, nhưng đó không phải là những gì nó hiện đang nói. Có lẽ đó là thời gian cho một DR. –

0

Dưới đây là một thử nghiệm đơn giản:

test_int.cpp:

const int test_int = 10; 

main.cpp:

#include <iostream> 
using std::cout; 
using std::endl; 

extern const int test_int; 

int main() { 
    cout << test_int << endl; 
    return 0; 
} 

Trong G ++, tôi nhận được một tài liệu tham khảo không xác định. Tuy nhiên, làm điều tương tự trong C hoạt động.Theo http://gcc.gnu.org/ml/gcc/2005-06/msg00325.html, một biến const ngầm có liên kết nội bộ trong C++. Đây không phải là trường hợp trong C.

+2

Bạn vẫn có thể tạo biến 'const' với liên kết ngoài trong C++ nếu bạn khai báo nó là' extern const' trước khi xác định. –

1

Trình biên dịch MS đang có chút nghịch ngợm ở đây. Khi bạn biên dịch khởi tạo hằng số và câu lệnh case bằng cách sử dụng hằng số trong cùng một đơn vị biên dịch, nó tính toán giá trị hằng số tại thời gian biên dịch.

Khi bạn cố gắng sử dụng các extern constngoài của đơn vị biên soạn mà nó được khởi tạo (ví dụ: tập tin cpp chứa khởi hoặc bất kỳ tập tin nó bao gồm) trình biên dịch sẽ barf với khá nhiều lỗi tương tự. Fred Larson là đúng trình biên dịch không nên biết giá trị không đổi cho đến khi liên kết thời gian và do đó nó phải không được chấp nhận như là một hằng số chuyển đổi, nó chỉ là trình biên dịch MS cheats một chút.

Giải pháp cho vấn đề của bạn là sử dụng macro, có bất kỳ lý do nào khiến bạn không muốn #define không đổi?

+1

Bạn thậm chí không phải # define nó. Một 'const int' với liên kết bên trong, được khởi tạo với một biểu thức liên tục, sẽ ổn. Điều này có thể được thực hiện trong một tiêu đề. –

+0

Thực tế là nó không hoạt động. Nó gần giống như nó phải là một phần của điều tra vì nó được sử dụng bên trong một câu lệnh switch. Khó nói mà không biết mục đích của chương trình. –

+0

Bạn thấy câu lệnh khởi tạo và trường hợp trong cùng một đơn vị biên dịch trong mã demo trong câu hỏi ở đâu? –

1

tôi không thể tái sản xuất này trên một ví dụ tầm thường sử dụng VC++ 2008:

test.cpp:

extern const int n; 
int main() { 
    switch (0) { 
    case n: break; 
    } 
} 

test2.cpp:

extern const int n = 123; 

biên dịch với:

cl.exe test.cpp test2.cpp 

đầu ra:

test.cpp(4) : error C2051: case expression not constant

+0

mô-đun phải được biên dịch riêng lẻ (không liên kết) thời gian hiển thị. hãy thử bao gồm test2.cpp trong test.cpp như OP và nó sẽ hoạt động. –

+0

Tôi không thấy '# include' của một .cpp thành một ví dụ khác. Anh ta #including header trong cả hai .cpp's, nhưng header chỉ có khai báo 'extern const', không phải là định nghĩa với initializer. –

0

Tôi đang sử dụng "gcc (SUSE Linux) 4.3.2" và có hiệu ứng tương tự, vẫn còn hơi lạ.

định nghĩa của tôi là:

namespace operations{ 
    const cOpDummy OpDummy(); 
    const cInitOperator InitOperator(); 
}; 

const unsigned long ulNumberOfOperations = 2; 

const cOperation * arrayOperations[] = { 
    & (operations::OpDummy), 
    & (operations::InitOperator) 
}; 

Và tờ khai extern trong một tập tin khác là:

extern const unsigned long ulNumberOfOperations; 
extern const cOperation * arrayOperations[]; 

Điều buồn cười là: Trình biên dịch cho chỉ dành riêng cho "ulNumberOfOperations" "tài liệu tham khảo không xác định để ulNumberOfOperations ", nhưng là Ok với" arrayOperations [] ". Cách giải quyết của tôi là khai báo "ulNumberOfOperations" không phải là hằng số.

0

Kể từ C++ 11 bạn có thể xây dựng một chút khung mẫu để cung cấp cho bạn một cú pháp như thế này:

void test(int a, int x, int y, int z) 
{ 
    std::cout << "given " << a << ", choosing "; 
    given(a) 
     .when(x, [] { std::cout << "x\n"; }) 
     .when(y, [] { std::cout << "y\n"; }) 
     .when(z, [] { std::cout << "z\n"; }) 
     .when(any_other, [] { std::cout << "none of the above\n"; }); 
} 

Full demo:

#include <iostream> 


struct any_object {}; 
constexpr auto any_other = any_object {}; 

template<class Expr> 
struct when_object 
{ 

    template<class T, class F> 
    constexpr when_object& when(T const& value, F&& f) 
    { 
     if (not executed and expr == value) { 
      executed = true; 
      f(); 
     } 
     return *this; 
    } 

    template<class F> 
    constexpr void when(any_object, F&& f) 
    { 
     if (not executed) { 
      executed = true; 
      f(); 
     } 
    } 

    Expr const& expr; 
    bool executed = false; 
}; 

template<class Expr> 
constexpr auto given(Expr const& expr) 
{ 
    return when_object<Expr> {expr}; 
} 

void test(int a, int x, int y, int z) 
{ 
    std::cout << "given " << a << ", choosing "; 
    given(a) 
     .when(x, [] { std::cout << "x\n"; }) 
     .when(y, [] { std::cout << "y\n"; }) 
     .when(z, [] { std::cout << "z\n"; }) 
     .when(any_other, [] { std::cout << "none of the above\n"; }); 
} 


int main() 
{ 
    test(4, 4, 5, 6); 
    test(4, 3, 4, 5); 
    test(4, 2, 3, 4); 
    test(1, 2, 3, 4); 
} 

kết quả mong đợi:

given 4, choosing x 
given 4, choosing y 
given 4, choosing z 
given 1, choosing none of the above 
Các vấn đề liên quan