2012-05-03 27 views
8

Tôi muốn viết một mẫu trả về cho tôi loại số nguyên có chữ ký nhỏ nhất có thể đại diện cho một số đã cho. Đây là giải pháp của tôi:GCC: so sánh luôn đúng do phạm vi hạn chế của kiểu dữ liệu - trong Tham số mẫu?

/** 
* Helper for IntTypeThatFits. 
* Template parameters indicate whether the given number fits into 8, 16 or 32 
* bits. If neither of them is true, it is assumed that it fits 64 bits. 
*/ 
template <bool fits8, bool fits16, bool fits32> 
struct IntTypeThatFitsHelper { }; 

// specializations for picking the right type 
// these are all valid combinations of the flags 
template<> struct IntTypeThatFitsHelper<true, true, true> { typedef int8_t Result; }; 
template<> struct IntTypeThatFitsHelper<false, true, true> { typedef int16_t Result; }; 
template<> struct IntTypeThatFitsHelper<false, false, true> { typedef int32_t Result; }; 
template<> struct IntTypeThatFitsHelper<false, false, false> { typedef int64_t Result; }; 

/// Finds the smallest integer type that can represent the given number. 
template <int64_t n> 
struct IntTypeThatFits 
{ 
    typedef typename IntTypeThatFitsHelper< 
     (n <= numeric_limits<int8_t>::max()) && (n >= numeric_limits<int8_t>::min()), 
     (n <= numeric_limits<int16_t>::max()) && (n >= numeric_limits<int16_t>::min()), 
     (n <= numeric_limits<int32_t>::max()) && (n >= numeric_limits<int32_t>::min()) 
    >::Result Result; 
}; 

Tuy nhiên, GCC không chấp nhận mã này. Tôi gặp lỗi "so sánh luôn đúng do phạm vi hạn chế của kiểu dữ liệu [-Werror = type-limits]". Tại sao điều đó lại xảy ra? n là một số nguyên 64 bit đã ký, và tất cả các phép so sánh có thể đúng hoặc sai cho các giá trị khác nhau của n, hoặc tôi có thấy cái gì đó không?

Tôi sẽ rất vui khi được trợ giúp.

Chỉnh sửa: Tôi nên đề cập rằng tôi đang sử dụng C++ 11.

+0

Làm cách nào có thể có _not_ nhỏ hơn 'max' _and_ không quá' phút '? Tôi có đọc sai không? –

+0

@SethCarnegie: Nó đang kiểm tra xem 'n' có nằm trong phạm vi cho ** loại dữ liệu ** khác nhau hay không. –

+0

Đối với những người muốn thử sức với nó, tôi đã quản lý để có được một phiên bản trên [ideone] (http://ideone.com/PhAJN) (không hỗ trợ 'constexpr' nào được nêu ra ...) –

Trả lời

4

Đó là vấn đề với gcc, cảnh báo trong mã templated có thể gây phiền toái. Bạn có thể thay đổi cảnh báo hoặc sử dụng cách tiếp cận khác.

Như bạn đã biết, mã templated được phân tích hai lần:

  • một lần khi lần đầu tiên gặp phải (phân tích)
  • một lần khi khởi tạo cho một loại nhất định/giá trị

Vấn đề ở đây là rằng tại instantiation, kiểm tra là tầm thường (có 65 phù hợp với một int cảm ơn bạn), và trình biên dịch không nhận ra rằng cảnh báo này không giữ cho tất cả instantiations: (Nó là rất bực bội thực sự cho những người trong chúng ta đã có trải nghiệm biên dịch cảnh báo miễn phí với cảnh báo trên.

Bạn có 3 khả năng:

  • Tắt cảnh báo này, hoặc demote nó để một tổ chức phi lỗi
  • sử dụng một pragma chọn lọc tắt nó cho mã này
  • rework mã trong định dạng khác để rằng nó không kích hoạt cảnh báo lâu hơn

Lưu ý rằng đôi khi khả năng thứ 3 liên quan đến thay đổi lớn và giải pháp phức tạp hơn nhiều. Tôi khuyên bạn không nên viết mã phức tạp chỉ để loại bỏ các cảnh báo không cần thiết.

EDIT:

Một thể workaround:

template <int64_t n> 
struct IntTypeThatFits { 
    static int64_t const max8 = std::numeric_limits<int8_t>::max(); 
    static int64_t const min8 = std::numeric_limits<int8_t>::min(); 

    static int64_t const max16 = std::numeric_limits<int16_t>::max(); 
    static int64_t const min16 = std::numeric_limits<int16_t>::min(); 

    static int64_t const max32 = std::numeric_limits<int32_t>::max(); 
    static int64_t const min32 = std::numeric_limits<int32_t>::min(); 

    typedef typename IntTypeThatFitsHelper< 
     (n <= max8) && (n >= min8), 
     (n <= max16) && (n >= min16), 
     (n <= max32) && (n >= min32) 
    >::Result Result; 
}; 

... bằng cách thay đổi kiểu dữ liệu được sử dụng trong việc so sánh, nó nên im lặng cảnh báo trình biên dịch. Tôi cho rằng đúc rõ ràng (int64_t(std::numeric_limits<int8_t>::max())) cũng có thể hoạt động, nhưng tôi thấy điều này dễ đọc hơn.

+0

Vì vậy, đây là một lỗi trong GCC? Sau khi tất cả, cảnh báo chỉ có ý nghĩa cho so sánh thời gian chạy. –

+0

@BenjaminSchug: Tôi sẽ không coi đó là lỗi * vì bạn có thể cho rằng nó hoạt động như mong đợi. Sau khi tất cả trong một số trường hợp bạn có thể ** muốn ** được cảnh báo tại thời điểm instantiation: x Nó chỉ là trong trường hợp của bạn nó có hại ... –

+0

Trong bối cảnh này, so sánh là _required_ là một hằng số biên dịch-thời gian, nếu không Tôi không thể sử dụng nó làm đối số mẫu. Vì vậy, nếu điều này thực sự là lý do cho các cảnh báo, làm thế nào mà không phải là một lỗi? –

1
typedef typename IntTypeThatFitsHelper< 
     (n <= numeric_limits<int8_t>::max()) && (n >= numeric_limits<int8_t>::min()), 
     (n <= numeric_limits<int16_t>::max()) && (n >= numeric_limits<int16_t>::min()), 
     (n <= numeric_limits<int32_t>::max()) && (n >= numeric_limits<int32_t>::min()) 
    >::Result Result; 

Bạn không thể làm điều đó trong C++ (trong C++ 11 bạn có thể) - numeric_limits<int8_t>::max() không phải là hằng số thời gian biên dịch. Bạn đang sử dụng C++ 11?

BTW, Boost cung cấp này cho bạn đã: http://www.boost.org/doc/libs/1_49_0/libs/integer/doc/html/boost_integer/integer.html

+0

Có, tôi đang sử dụng C++ 11. Nên đã đề cập rằng, xin lỗi. –

+0

Cảm ơn bạn đã tip với Boost, điều đó dường như hiệu quả. Tôi thực sự nên luôn luôn kiểm tra Boost trước khi viết một cái gì đó bản thân mình. :-) –

2

Các lỗi đang xảy ra bởi vì bạn hỏi GCC cung cấp cho bạn các lỗi về cảnh báo này với -Werror=type-limits. Cảnh báo -Wtype-limits mang đến cho bạn một cảnh báo nếu bạn đã bao giờ làm một so sánh mà sẽ luôn luôn là sự thật do phạm vi của các kiểu dữ liệu nhất định, ví dụ:

uint8_t x; 
if(x >= 0) { ... } // always true, unsigned integers are non-negative 
if(x >= 256) { ... } // always false 

int32_t x; 
if(x < 9223372036854775808LL) { ... } // always true 

Cảnh báo này đôi khi có thể hữu ích, nhưng trong nhiều trường hợp bao gồm điều này nó chỉ là vô dụng pedantry và có thể được bỏ qua. Nó thường là một cảnh báo (được kích hoạt như một phần của -Wextra, nếu bạn sử dụng nó), nhưng với -Werror hoặc -Werror=type-limits, GCC làm cho nó là một lỗi.

Vì trong trường hợp này nó không thực sự là dấu hiệu của một vấn đề tiềm năng với mã của bạn, chỉ cần tắt cảnh báo với -Wno-type-limits, hoặc làm cho nó không phải là lỗi với Werror=no-type-limits nếu bạn không thấy những cảnh báo đó trong đầu ra trình biên dịch .

+0

Ở đâu trong mã OP có sự so sánh luôn đúng do phạm vi của các kiểu dữ liệu? – interjay

+0

Tôi không muốn tắt hoàn toàn cảnh báo đó vì nó hữu ích trong trường hợp thông thường.Ở đây nó chỉ nên không kích hoạt vì so sánh đó là _supposed_ và _required_ là một hằng số thời gian biên dịch. –

0

Bạn nhận được cảnh báo bởi vì đối với một số instantations của template <int64_t n> struct IntTypeThatFits với nhỏ n (nhỏ hơn 2^32) một số so sánh là luôn luôn đúng (sic!) Vì phạm vi của toán hạng trong thời gian biên dịch.

Điều này có thể được coi là cảnh báo không có thật trong trường hợp này, vì mã của bạn dựa vào nó, OTOH bạn đã yêu cầu báo lỗi là -Werror hoặc công tắc dòng lệnh tương tự, về cơ bản bạn nhận được những gì bạn yêu cầu ở đây.

+0

* về cơ bản bạn có được những gì bạn yêu cầu ở đây * -> Tôi không đồng ý. Nó là một cảnh báo hữu ích trong nhiều tình huống, và có rất nhiều sự tức thời của mẫu mà nó sẽ không cháy. Tôi muốn các trình biên dịch sẽ tránh những cảnh báo như vậy khi chúng không giữ cho mọi khoảnh khắc ... mặc dù tôi nhận ra nó phức tạp để đánh giá. –

+0

IMHO thường sử dụng '-Werror' chỉ yêu cầu sự cố. Cảnh báo là một điều tốt, nhưng có nhiều cảnh báo không có thật trong gcc ra khỏi đó để làm cho chúng bị xói mòn mọi lúc. Đặc biệt với mã templated được đánh giá trong thời gian biên dịch. – hirschhornsalz

+0

Tôi không gặp vấn đề gì với '-Werror' trong mã của tôi, và đây là danh sách cảnh báo của tôi trông như thế nào: http://stackoverflow.com/questions/5088460/flags-to-enable-thorough-and-verbose-g- cảnh báo/9862800 # 9862800 (Tôi thỉnh thoảng bật những cảnh báo khác mà tôi đề cập đến nhưng nói rằng tôi không sử dụng). –

1

Tôi nghĩ rằng các câu trả lời khác của vấn đề là gì sai. Tôi không tin rằng đây là một trường hợp của một trình biên dịch quá mong muốn, nhưng tôi tin rằng đó là một lỗi trình biên dịch. Mã này vẫn bắn cảnh báo:

template<int64_t n> 
bool a() { 
    return (n <= static_cast<int64_t>(std::numeric_limits<int8_t>::max())); 
} 

Khi gọi a<500>();, nhưng mã này không:

template<int64_t n> 
bool a() { 
    return (n <= static_cast<int64_t>(127)); 
} 

std :: numeric_limits :: max() trả về 127. Tôi sẽ nộp bugzilla báo cáo cho điều này sau ngày hôm nay nếu không có ai khác làm.

+0

Bạn có thể xem xét một trình biên dịch quá mong muốn trình biên dịch lỗi :-) – hirschhornsalz

+0

Tôi vừa gửi báo cáo lỗi đến danh sách gửi thư của họ: http://gcc.gnu.org/ml/gcc-bugs/2012-05/msg00311.html –

+1

@BenjaminSchug Sử dụng tốt hơn bugzilla lần sau http : //gcc.gnu.org/bugzilla/. Bất cứ điều gì báo cáo có được gửi đến danh sách gửi thư anyway. – hirschhornsalz

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