Search code examples
c++c++11constexprunused-variables

How to use UNUSED macro to silence warning within a CONSTEXPR function?


I'm experiencing an issue where a static member function uses an UNUSED macro to silence compiler warnings. When the macro is in effect, it causes GCC and Clang to reject the function as a constexpr. Here's the test case:

$ 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;
}

Here's the GCC error message:

$ 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
     }
     ^

If I remove the use of UNUSED, then the source file compiles as expected:

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

As far as I know, the #define UNUSED(x) ((void)x) is the only portable way to suppress unused variable warnings. I dread removing UNUSED because the macro suppresses thousands of warnings in a non-trivial C++ project with lots of interfaces. I'm not even sure I can remove UNUSED because of governance issues related to auditing and C&A.

How can I make the UNUSED macro work and play well with constexpr?


Clang produces a more helpful error message:

$ 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.

Another twist when moving from the clean room to production: Doxygen. This is closer to what happens in practice, so we can't omit the variable name.

//! \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;
}

Solution

  • In C++11 you have some limitations on the body of a constexpr function.

    In your specific case you can use the comma operator to overcome them:

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

    In C++14, your function is ok as it is.