2013-08-27 61 views
5

Tôi đang biên soạn một số mã với kêu vang 3.3 mà dường như để biên dịch tốt với gcc 4.8:kêu vang 3.3 và constexpr trở ngại

Mã ban đầu là:

template <std::size_t N> struct helper { typedef void type; }; 
template <> struct helper<64> { typedef int64_t type; }; 
template <> struct helper<32> { typedef int32_t type; }; 
template <> struct helper<16> { typedef int16_t type; }; 
template <> struct helper<8> { typedef int8_t type; }; 

template <std::size_t I, std::size_t F> 
struct test 
{ 
    typedef typename helper<I+F>::type value_type; 
    static constexpr std::size_t frac_mask = ~((~value_type(0)) << F); 
}; 

Trong kêu vang, nếu tôi cố gắng để khai báo kiểm tra < 16,16> hoặc kiểm tra < 8.0> tôi nhận được lỗi:

test.cpp:41:34: error: constexpr variable 'frac_mask' must be initialized by a constant expression

static constexpr std::size_t frac_mask = ~((~value_type(0)) << F); 

Chơi đùa với nó, nếu tôi chuyển đổi mã để:

template <std::size_t I, std::size_t F> 
struct test 
{ 
    typedef typename helper<I+F>::type value_type; 
    typedef typename std::make_unsigned<value_type>::type mask_type; 

    static constexpr mask_type frac_mask = ~((~mask_type(0)) << F); 
}; 

Nó biên dịch ở hầu hết các trường hợp (giá trị của I, F), nhưng nếu tôi tuyên bố thử nghiệm < 8, 0>, tôi nhận được lỗi:

test.cpp:23:36: error: constexpr variable 'frac_mask' must be initialized by a constant expression

test.cpp:66:15: note: in instantiation of template class 'test<8, 0>' requested here

test.cpp:23:66: note: left shift of negative value -1

static constexpr mask_type frac_mask = ~((~mask_type(0)) << F); 

Câu hỏi của tôi là - là có một số quy tắc Tôi vi phạm ở đây về đặc điểm kỹ thuật của constexpr? Ngoài ra, đối với lỗi cuối cùng - loại mặt nạ không được ký - đây có phải là vấn đề về trình biên dịch mà nó cho rằng tôi đang chuyển một giá trị âm hay tôi đang đọc sai mã?

+0

Có thể trùng lặp của [Gọi constexpr trong đối số mẫu mặc định] (http://stackoverflow.com/questions/10721130/calling-constexpr-in-default-template-argument) –

+0

Nhập quy tắc quảng cáo chuyển đổi uint8_t thành int, được ký? –

Trả lời

3

Trong trường hợp đầu tiên, bạn đang gây ra tràn đăng nhập. Một trong những điều kiện để một biểu không trở thành một biểu thức hằng số, được liệt kê trong C++ 11 5,19/2, là nó liên quan đến

a result that is not mathematically defined or not in the range of representable values for its type

Bằng cách sử dụng một loại unsigned, được định nghĩa để sử dụng số học modula , kết quả vẫn nằm trong phạm vi. Có lẽ, GCC là ít nghiêm ngặt về quy tắc này hơn so với Clang.

Trong trường hợp cuối cùng, loại 8 bit chưa ký được thăng cấp thành int, không phải loại không dấu, do đó bạn sẽ bị tràn lại lần đăng nhập. Bạn có thể khắc phục điều đó bằng cách chuyển đổi về loại không dấu sau khi phủ nhận:

static constexpr mask_type frac_mask = ~(mask_type(~mask_type(0)) << F); 

mặc dù tôi không chắc chắn về điều đó và không cài đặt Clang để kiểm tra.

+0

Điều đó dường như sửa chữa nó - bạn có thể giải thích cho tôi tại sao bitwise không phải của một loại unsigned sẽ được thúc đẩy để int? – user2721897

+0

@ user2721897: Quy tắc quảng cáo chỉ định rằng nó được quảng bá thành 'int' nếu có thể biểu thị tất cả các giá trị của loại ban đầu; đó là trường hợp cho bất kỳ loại nhỏ hơn, ký hoặc unsigned. –

+0

@ user2721897 char unsigned được đẩy vào int * trước * ~ (hoặc bất kỳ toán tử số học nào) được áp dụng. Điều này có nghĩa là ví dụ: 'intvar greggo