【问题标题】:How to use UNUSED macro to silence warning within a CONSTEXPR function?如何使用 UNUSED 宏来消除 CONSTEXPR 函数中的警告?
【发布时间】:2017-01-12 11:21:12
【问题描述】:

我遇到了静态成员函数使用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++ 项目中抑制了数千个警告。由于与审计和 C&A 相关的治理问题,我什至不确定是否可以删除 UNUSED

如何使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;
}

【问题讨论】:

  • 可能是return UNUSED(n), DEF_N;?无论如何,为什么这个函数需要n
  • @user2357112 - 它是一个接口的MCVE。真正的代码更有趣一点

标签: c++ c++11 constexpr unused-variables


【解决方案1】:

您可以简单地避免给参数命名,或者将其注释掉:

constexpr size_t DEF_N = 42;

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

live demo

【讨论】:

  • 谢谢@kfsone。这种破坏 Doxygen。
  • 您可以编写一个以 doxygen 的预处理器符号为条件的宏,当 doxygen 处于活动状态时定义为 #define DOCNAME(x) x,而在不活动时定义为 #define DOCNAME(x)
【解决方案2】:

在 C++11 中,constexpr 函数的主体有一些限制。

在您的具体情况下,您可以使用逗号运算符来克服它们:

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

在 C++14 中,您的函数可以正常使用。

【讨论】:

  • 此更改的初始冒烟测试进展顺利;我们在Add constexpr-ness to seckey.h classes 提交了它。我将返回反馈,因为这些更改在编译器(Clang、Comeau、GCC、ICC、MSVC、SunCC)和平台(Android、iOS、Linux、Solaris、Windows {Desktop|Server|Phone|Store})下得到了更彻底的测试,等)。
  • 您是否会知道assert 是否会引发类似问题?断言是我喜欢的另一个工具,我希望调试和诊断助手不会在开发过程中排除 C++11 功能。
  • @jww 您希望断言会准确触发什么?我不明白你想要什么,对不起。如果你能给我更多的细节,我会尽力给你一个答案。谢谢。
  • 我想知道assert 是否会导致编译失败。
  • @jww 如果你的意思是static_assert,它计算一个常量表达式。只要你能提供它(例如使用constexpr函数)你应该没有问题。
【解决方案3】:

最简单的解决方案是只注释掉 kfsone 提到的 n。

在 C++17 中,你甚至可以这样做:

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

欲了解更多信息,请参阅http://en.cppreference.com/w/cpp/language/attributes

我不确定这是否是一种风格上合理的解决方案,希望 IDE 能找到一种方法让它看起来不那么难看。

【讨论】:

  • 注释掉 n 会破坏 Doxgen 文档。我对 C++17 的东西没问题,但是我们支持 pre-C++03(GCC 3 和 VC++ 2002),通过你今天可能遇到的任何事情(我们积极地评估 C++14 和 C++17 下的中断) )。我不确定让 [[maybe_unused]] 适应 20 年的编译器有多么容易。
  • @jww 如果你能接受它的外观,你可以做我提到的另一件事。不过,您将需要一个现代编译器(MSVC 应该已经支持这个 IIRC,不确定 clang/gcc)。
猜你喜欢
  • 2021-04-21
  • 1970-01-01
  • 2017-08-31
  • 2014-06-07
  • 2014-05-31
  • 2012-08-25
  • 1970-01-01
  • 1970-01-01
  • 2014-01-18
相关资源
最近更新 更多