使用{}进行统一初始化时报告未使用的变量

6

使用g++ 4.7.0 (-Wall -Wextra -Werror -Wconversion -std=c++11) 编译此代码:

#include <iostream>  // std::cout, std::endl
#include <string>    // std::string
#include <utility>   // std::move

void out(std::string const &message)
{
   static int count{0};
   std::cout << count++ << " = " << message << std::endl;
}

struct Foo
{
   Foo()                         {out("constructor");}
  ~Foo()                         {out("destructor");}
   Foo(Foo const &)              {out("copy constructor");}
   Foo & operator=(Foo const &)  {out("copy via assignment"); return *this;}
   Foo(Foo &&)                   {out("move constructor");}
   Foo & operator=(Foo &&)       {out("move via assignment"); return *this;}
};

int main()
{
   auto bar{std::move(Foo())};
   out("exiting main");
}

...导致以下错误:

error: unused variable 'bar' [-Werror=unused-variable]

我可以通过将bar的初始化更改为以下任何一种来消除错误:

/* 0 */ auto bar(std::move(Foo()));
/* 1 */ Foo bar{std::move(Foo())};
/* 2 */ Foo bar(std::move(Foo()));
/* 3 */ auto bar = std::move(Foo());
/* 4 */ Foo bar = std::move(Foo());
/* 5 */ auto bar __attribute__((unused)) {std::move(Foo())};

一旦bar初始化被更改,输出结果将始终如下:

0 = constructor
1 = move constructor
2 = destructor
3 = exiting main
4 = destructor

为什么原始的bar初始化会报告未使用的变量?

如果您在编译时没有使用-Werror选项,而使用了“错误”的代码版本,那么在运行时会发生什么? - John Zwinck
@JohnZwinck 这个 错误 变成了一个 警告 - Matt Tardiff
似乎这只是未处理的情况在未使用变量警告逻辑中。请向gcc提交错误报告。 - bames53
我说的是“在运行时”。我的意思是请在编译程序时不要使用-Werror选项运行程序,然后告诉我们你看到了什么。它是否与代码的其他版本执行相同,还是什么都没有打印出来? - John Zwinck
@JohnZwinck 抱歉我的错误。在运行时,它会输出与其他版本相同的结果。 - Matt Tardiff
请注意:除非在最近的版本中进行了更改,否则gcc检测未使用变量依赖于优化通道。由于应用的优化通道可能因生成IR(Gimple?)的方式而异,而这本身可能取决于变量的声明/初始化方式,因此我对新语法可能存在一些怪癖并不感到意外。如果您报告它,它很可能会被修复。 - Matthieu M.
2个回答

8
auto bar{std::move(Foo())};

在这个声明之后,bar 是类型为std::initializer_list<Foo>的变量,该变量具有平凡的复制/移动操作和析构函数。你的其他声明。
auto bar(std::move(Foo()));
Foo bar{std::move(Foo())};
Foo bar(std::move(Foo()));
auto bar = std::move(Foo());
Foo bar = std::move(Foo());

bar声明为Foo或者Foo&&可以抑制警告,因为它具有非平凡的特殊成员函数。

通常情况下不希望使用auto进行大括号初始化,除非您特别打算创建一个std::initializer_list对象。


这很有道理。我错误地假设GCC会将Foo用作类型,而实际上它没有理由这样做。 - Matt Tardiff
1
事实上,我尝试了 auto bar{5}; cout << typeid(bar) << endl;,它输出了 St16initializer_listIiE,所以你肯定是正确的! - Matt Tardiff

2

嗯,bar 没有被使用。 如果您认为这是其他情况下未能检测到的错误,请向编译器提交一个缺陷报告。


2
“bar” 没有被使用,但我认为编译器不会对非平凡类型报告未使用变量警告。 - Matt Tardiff
无论编译器使用什么来产生警告,都取决于编译器!我的个人立场是,当我要求标记未使用的变量时,我希望所有未使用的变量都被标记。在我的工具包中,我有一个简单的函数模板,用于那些我意味着对象未使用的情况:template <typename T> void use(T const&) {} - Dietmar Kühl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接