Подавить предупреждение, что сравнение всегда ложно из-за того, что константа равна нулю - PullRequest
0 голосов
/ 18 октября 2018

Я нашел код этой формы в локальном проекте:

#include <iostream>

const unsigned LOWER_LIMIT = 0;

int main(int argc, char* argv[])
{
    if (argc > 1) {
        unsigned a = atoi(argv[1]);
        if (a < LOWER_LIMIT) {
        // if (a - LOWER_LIMIT > a) {
            std::cerr << "lower\n";
            a = LOWER_LIMIT;
        }
    }
}

atoi() часть предназначена только для запуска демо и поставляется из другой частипрограммное обеспечение в реальном случае.Естественно, этот код выдает предупреждение с недавним g++ -Wall -Wextra:

comparison of unsigned expression < 0 is always false

Сопровождающий не хочет изменять сравнение, потому что константа LOWER_LIMIT может быть изменена в будущем, и он хочет, чтобы она по-прежнему работала без изменений,Кроме того, он хочет, чтобы тип тоже был без знака.Моя первая идея подавить предупреждение -

if (a - LOWER_LIMIT > a) {

, в котором используется неподписанный нижний предел.Но это решение несколько сложно понять для некоторых людей.Я нашел другое решение:

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits"
if (a < LOWER_LIMIT) {
#pragma GCC diagnostic pop

Это работает, но из части GCC я предполагаю, что это не переносимое решение.

Существует ли портативное, универсальное и простое в использовании решение?Понимаете, как избежать этого предупреждения, и при этом сохраняете возможность для некоторых разработчиков просто изменять константу, не зная об этой части базы кода, где происходит сравнение?

РЕДАКТИРОВАТЬ: У меня есть шаблонное решение для этого, но оно выглядит как излишнее:

template<typename NumericTypeA, typename NumericTypeB>
typename std::enable_if<std::is_unsigned<NumericTypeA>::value &&
            std::is_unsigned<NumericTypeB>::value, bool >::type
is_smaller_than(NumericTypeA a, NumericTypeB b)
{
    return (a - b > a);
}

template<typename NumericTypeA, typename NumericTypeB>
typename std::enable_if<std::is_signed<NumericTypeA>::value &&
            std::is_signed<NumericTypeB>::value, bool >::type
is_smaller_than(NumericTypeA a, NumericTypeB b)
{
    return (a < b);
}

РЕДАКТИРОВАТЬ 2: Если компилятор обнаружит, что сравнение всегда ложно, не будет ли это необходимо длячтобы проверить следующее утверждение и обнаружить, что это нормально (также предлагается в ответе)?

if ((LOWER_LIMIT > 0) && (a < LOWER_LIMIT)) {

Такое чувство, что компилятор должен принять это без предупреждения, но, к сожалению, он предупреждает в этомслучай.

Ответы [ 3 ]

0 голосов
/ 18 октября 2018

Возможно, есть способы обойти проверку компилятора с дополнительным слоем:

if (std::less<>()(a, LOWER_LIMIT)) // extra function

Возможно, излишнее количество с шаблоном:

template <std::size_t Limit>
struct DoesIfLess
{
    template <typename F>
    void operator ()(std::size_t a, F&&f) {
        if (a < Limit) {
            std::forward<F>(f)();
        }
    }
};

template <>
struct DoesIfLess<0>
{
    template <typename F>
    void operator ()(std::size_t, F&&) {}
};

И затем

DoesIfLess<LOWER_LIMIT>(a, [&](){std::cerr << "lower\n"; a = LOWER_LIMIT; });

или с C ++ 17

if constexpr (LOWER_LIMIT != 0) {
    if (a < LOWER_LIMIT) {
        std::cerr << "lower\n";
        a = LOWER_LIMIT;
    }
}
0 голосов
/ 18 октября 2018

Если вы оберните свою константу в функцию «ничего не делать», которая просто возвращает значение константы, предупреждение должно исчезнуть.

#include <iostream>

const unsigned LOWER_LIMIT = 0;

constexpr auto get_lower_limit() { return LOWER_LIMIT; }

int main(int argc, char* argv[])
{
    if (argc > 1) {
        unsigned a = atoi(argv[1]);

        if (a < get_lower_limit()) {
            std::cerr << "lower\n";
            a = LOWER_LIMIT;
        }
    }
}
0 голосов
/ 18 октября 2018

Есть ли портативная #pragma?

Нет.#pragma по определению определяется реализацией.

Параметры предупреждений, в частности, сильно различаются в разных компиляторах.

Сопровождающий не хочет ...

Поскольку задействован сопровождающий, я предполагаю, что рассматриваемый код находится во включенном заголовке, предоставленном третьей стороной.

Вы можете настроить свой компилятор так, чтобы он не сообщал о предупреждениях от сторонних заголовков.В случае GCC вы можете использовать опцию -isystem вместо -I.Таким образом, вам не нужно беспокоиться о их предупреждениях.

Если вы хотите поддерживать несколько платформ, вам, вероятно, потребуется использовать кроссплатформенный генератор систем сборкитакие как cmake.В cmake вы можете назначить системные директории с помощью опции SYSTEM - хотя не все системы обязательно поддерживают эту концепцию.

...