ChatGPT解决这个技术问题 Extra ChatGPT

c++11中未使用的参数

在 c++03 及更早版本中,为了禁用关于未使用参数的编译器警告,我通常使用这样的代码:

#define UNUSED(expr) do { (void)(expr); } while (0)

例如

int main(int argc, char *argv[])
{
    UNUSED(argc);
    UNUSED(argv);

    return 0;
}

但是宏不是 C++ 的最佳实践,所以。 c++11标准有没有更好的解决方案?我的意思是我可以摆脱宏吗?

谢谢大家!

当然。关闭警告。
不!不要那样做!
该宏比内联扩展它好多少? (void)argc;UNUSED(argc); 更短更清晰
我喜欢 unused(argc, argv)template<class... T> void unused(T&&...){}。清晰,简洁,没有宏。
@MadScientist 但您可以留下未命名的参数,甚至只是注释掉它的名称。 void foo(int /*unused_arg*/, int used_arg)

M
Micha Wiedenmann

您可以省略参数名称:

int main(int, char *[])
{

    return 0;
}

在 main 的情况下,您甚至可以完全省略参数:

int main()
{
    // no return implies return 0;
}

请参阅 C++11 标准中的“§ 3.6 开始和终止”。


main 的情况下,您可以完全省略参数。和 return 声明,就此而言。
@MikeSeymour 我实际上认为省略 return 语句是一种好习惯。
@jotep 好的,我会咬的。为什么你认为这是好的做法?
我几乎总是在测试用例中省略 mainreturn 0,但几乎总是在生产代码中编写自记录 return EXIT_SUCCESS这是的好习惯!
这对我来说似乎是最好的答案——任何与宏或模板有关的东西仍然不能确保以后不能使用该变量。这既可以使警告静音,又可以确保永远不能使用(未命名的)参数。
w
woolstar

C++11 中有 <tuple>,其中包括准备使用的 std::ignore 对象,它允许我们编写(很可能不会产生运行时开销):

void f(int x)
{
    std::ignore = x;
}

考虑到这是在标准库中,因此不需要编写自定义函数,我会说这是最好的解决方案!
此类是“用于在解压 std::tuple 时与 std::tie 一起使用”,不适用于此用例。我会说这是一个解决方案,但可能不是最好的。
我实际上喜欢这个忽略.. Ofc,如果您可以删除参数,请改为删除它(这将是所有情况下的最佳解决方案)。
M
MadScientist

为此,我使用了一个带有空主体的函数:

template <typename T>
void ignore(T &&)
{ }

void f(int a, int b)
{
  ignore(a);
  ignore(b);
  return;
}

我希望任何认真的编译器都能优化函数调用,并为我消除警告。


T 是模板参数时,T&& 是绑定到任何东西的通用引用
+1即使他的评论中没有提到Xeo的高级版本。
为什么忽略内置方法?只需省略参数名称。
-1,这是荒谬且不必要的装置,尤其是当您可以省略参数名称时。坦率地说,这让我很困扰,因为它有 25 个赞成票。
@TC1 这使您的代码明确说明它的作用和原因。有未使用的参数或变量是代码中的一种气味,这使它变得明确。关闭警告会让你的代码更臭。
N
Nikko

要“禁用”这个警告,最好避免写参数,只写类型。

void function( int, int )
{
}

或者,如果您愿意,请将其注释掉:

void function( int /*a*/, int /*b*/ )
{
}

您可以混合命名和未命名的参数:

void function( int a, int /*b*/ )
{
}

使用 C++17 你有 [[maybe_unused]] 属性说明符,比如:

void function( [[maybe_unused]] int a, [[maybe_unused]] int b )
{
}

我曾经这样做,但很快就变得很痛苦,因为您不能再用 /* ... */ 注释掉大块代码
确实如此,但是对于现代 IDE,我猜如果您选择一个块来自动注释,它会在每行的开头添加一堆“//”。这就是 Eclipse CDT 所做的。就个人而言,我只使用第一个没有名字的例子。 (例如,您可以将名称放在 .h 文件中的声明中)。
@Wallacoloo 当我想注释掉大块代码时,我使用#if 0 ... #endif,它们可以嵌套并且永远不会与现有的 /* ... */ 注释冲突。
@DmitryFrank 大多数编辑器和 IDE 都支持将 #if 0 块灰显作为一种特殊情况,即使它们不支持完整的预处理器智能感知。
L
Lightness Races in Orbit

没有什么等价的,不。

所以你坚持使用相同的旧选项。您愿意完全省略参数列表中的名称吗?

int main(int, char**)

当然,在 main 的特定情况下,您可以简单地省略参数本身:

int main()

还有一些典型的特定于实现的技巧,例如 GCC 的 __attribute__((unused))


j
jcayzac

你有什么反对旧的和标准的方式?

void f(int a, int b)
{
  (void)a;
  (void)b;
  return;
}

我发现有些编译器对此很满意,但有些编译器比其他编译器更挑剔。跨平台工作需要在所有目标操作系统和编译器中进行测试,以确保他们都对解决方案感到满意。
M
Mats Petersson

宏可能并不理想,但它们在这个特定目的上做得很好。我会说坚持使用宏。


+1:在这种情况下,它们造成零伤害并解决问题。我看不出有任何理由(除了“永远不要使用宏”的荒谬毫无根据的口头禅)不在这里使用它们。
与完全省略参数名称相比,宏有什么好处?
@MichaWiedenmann:某些参数只能在设置某些预处理常量时使用(通常在调试中)。
@MatthieuM.:出于这个原因,我将调用宏 MAYBE_UNUSED;我通常不在乎我是否说过“如果我在下面不使用它,请不要担心”,但无论如何都要继续这样做。
好的,所以正确的做法可能是称之为“HIDE_UNUSED_WARNING”。但我仍然认为在这里使用宏是一个完全有效的想法。只要宏的命名方式不会引起混淆和/或与其他代码冲突。
m
manlio

Boost 标头 <boost/core/ignore_unused.hpp> (Boost >= 1.56) 为此目的定义了函数模板 boost::ignore_unused()

int fun(int foo, int bar)
{
  boost::ignore_unused(bar);
#ifdef ENABLE_DEBUG_OUTPUT
  if (foo < bar)
    std::cerr << "warning! foo < bar";
#endif

  return foo + 2;
}

PS C++17 具有 [[maybe_unused]] 属性来抑制未使用实体的警告。


[[maybe_unused]] 是显式方式,目前最好。
A
Angew is no longer proud of SO

没有什么新东西可用。

最适合我的是在实现中注释掉参数名称。这样,您就可以摆脱警告,但仍然保留有关参数是什么的一些概念(因为名称可用)。

您的宏(以及所有其他强制转换为 void 的方法)的缺点是您可以在使用宏后实际使用该参数。这会使代码更难维护。


s
steeveeet

我真的很喜欢为此使用宏,因为它可以让您更好地控制何时有不同的调试版本(例如,如果您想在启用断言的情况下进行构建):

#if defined(ENABLE_ASSERTS)
  #define MY_ASSERT(x) assert(x)
#else
  #define MY_ASSERT(x)
#end

#define MY_UNUSED(x)

#if defined(ENABLE_ASSERTS)
  #define MY_USED_FOR_ASSERTS(x) x
#else
  #define MY_USED_FOR_ASSERTS(x) MY_UNUSED(x)
#end

然后像这样使用它:

int myFunc(int myInt, float MY_USED_FOR_ASSERTS(myFloat), char MY_UNUSED(myChar))
{
  MY_ASSERT(myChar < 12.0f);
  return myInt;
}

A
Andry

对于时间关键的代码段,我有自己的实现。我一直在研究用于减速的时间关键代码,发现这个实现消耗了我正在优化的时间关键代码的 2%:

#define UTILITY_UNUSED(exp) (void)(exp)
#define UTILITY_UNUSED2(e0, e1) UTILITY_UNUSED(e0); UTILITY_UNUSED(e1)
#define ASSERT_EQ(v1, v2) { UTILITY_UNUSED2(v1, v2); } (void)0

时间关键代码已将 ASSERT* 定义用于调试目的,但在发布时它显然已被删除,但是......似乎这个在 Visual Studio 2015 Update 3 中产生了更快的代码:

#define UTILITY_UNUSED(exp) (void)(false ? (false ? ((void)(exp)) : (void)0) : (void)0)
#define UTILITY_UNUSED2(e0, e1) (void)(false ? (false ? ((void)(e0), (void)(e1)) : (void)0) : (void)0)

原因在于双 false ? 表达式。它以某种方式在发布时产生了更快的代码,并具有最大的优化。

我不知道为什么这更快(似乎是编译器优化中的一个错误),但对于这种代码情况,它至少是一个更好的解决方案。

注意:这里最重要的是,时间关键型代码会在没有上述断言或发布中未使用的宏的情况下减慢速度。换句话说,双 false ? 表达式惊人地有助于优化代码。


佚名

windows.h 定义了 UNREFERENCED_PARAMETER:

#define UNREFERENCED_PARAMETER(P) {(P) = (P);}

所以你可以这样做:

#include <windows.h>
#include <stdio.h>
int main(int argc, char **argv) {
  UNREFERENCED_PARAMETER(argc);
  puts(argv[1]);
  return 0;
}

或在 Windows 之外:

#include <stdio.h>
#define UNREFERENCED_PARAMETER(P) {(P) = (P);}
int main(int argc, char **argv) {
  UNREFERENCED_PARAMETER(argc);
  puts(argv[1]);
  return 0;
}

这不是一个很好的选择,因为 operator= 可能有副作用。