У меня возникла проблема, когда статическая функция-член использует макрос UNUSED для отключения предупреждений компилятора. Когда макрос действует, он заставляет GCC и Clang отклонять функцию как constexpr. Вот тестовый пример:

$ cat test.cxx
#include <iostream>
#include <stdint.h>

#define UNUSED(x) ((void)x)

template <unsigned int N>
class Foo
{
public:
    enum {MIN_N=N}; enum {MAX_N=N}; enum {DEF_N=N};
    constexpr static size_t GetValidN(size_t n)
    {
        UNUSED(n); return DEF_N;
    }
};

class Bar : public Foo<16>
{
public:
    Bar(size_t n) : m_n(GetValidN(n)) {}
    size_t m_n;
};

int main(int argc, char* argv[])
{
    Bar b(10);
    return 0;
}

Вот сообщение об ошибке GCC:

$ g++ -std=c++11 test.cxx -o test.exe
test.cxx: In instantiation of ‘static constexpr size_t Foo<N>::GetValidN(size_t) [with unsigned int N = 16u; size_t = long unsigned int]’:
test.cxx:22:25:   required from here
test.cxx:16:5: error: body of constexpr function ‘static constexpr size_t Foo<N>::GetValidN(size_t) [with unsigned int N = 16u; size_t = long unsigned int]’ not a return-statement
     }
     ^

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

constexpr static size_t GetValidN(size_t n)
{
    return DEF_N;
}

Насколько мне известно, #define UNUSED(x) ((void)x) - единственный переносимый способ подавить предупреждения unused variable . Я боюсь удалять UNUSED, потому что макрос подавляет тысячи предупреждений в нетривиальном проекте C ++ с большим количеством интерфейсов. Я даже не уверен, что смогу удалить UNUSED из-за проблем управления, связанных с аудитом и C&A.

Как я могу заставить макрос UNUSED работать и работать с constexpr?


Clang выдает более полезное сообщение об ошибке:

$ clang++ -std=c++11 test.cxx -o test.exe
test.cxx:15:2: warning: use of this statement in a constexpr function is a C++14
      extension [-Wc++14-extensions]
        UNUSED(n); return DEF_N;
        ^
test.cxx:4:19: note: expanded from macro 'UNUSED'
#define UNUSED(x) ((void)x)
                  ^
1 warning generated.

Еще один поворот при переходе от чистого помещения к производству: Doxygen. Это ближе к тому, что происходит на практике, поэтому мы не можем пропустить имя переменной.

//! \brief Returns a valid N
//! \param n a value to determine a valid N
//! \returns a valid N
constexpr static size_t GetValidN(size_t n)
{
    return DEF_N;
}
3
jww 5 Сен 2016 в 08:56

3 ответа

Лучший ответ

В C ++ 11 у вас есть некоторые ограничения на тело функции constexpr.

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

constexpr static size_t GetValidN(size_t n)
{
    return UNUSED(n), DEF_N;
}

В C ++ 14 с вашей функцией все в порядке.

2
skypjack 5 Сен 2016 в 07:17

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

constexpr size_t DEF_N = 42;

constexpr static size_t GetValidN(size_t /*n*/)
{
    return DEF_N;
}

живая демонстрация

3
kfsone 5 Сен 2016 в 06:17

Самым простым решением было бы просто закомментировать n, как упоминалось в kfsone.

В C ++ 17 это можно было сделать даже так:

constexpr static size_t GetValidN([[maybe_unused]] size_t n)
{
    return DEF_N;
}

Для получения дополнительной информации см. http://en.cppreference.com/w/cpp/language/attributes

Я не уверен, что это стилистически разумное решение, надеюсь, IDE найдут способ сделать его менее уродливым.

0
5 Сен 2016 в 06:26