cYr*_*rus 17 c++ compiler-warnings
struct X {
X (int x) {}
};
int main() {
X x1(0); // nothing!
X x2 = 0; // warning: unused variable
X x3 = X(0); // warning: unused variable
}
Run Code Online (Sandbox Code Playgroud)
为什么没有生成警告x1?
我正在编译-Wall选项.
GCC和Clang都产生相同的输出.
这3行生成相同的汇编指令(asm("nop")为清楚起见放置一些):
nop
lea -0x8(%rbp),%rdi
mov $0x0,%esi
callq 400600 <X::X(int)>
nop
lea -0x10(%rbp),%rdi
mov $0x0,%esi
callq 400600 <X::X(int)>
nop
lea -0x18(%rbp),%rdi
mov $0x0,%esi
callq 400600 <X::X(int)>
nop
Run Code Online (Sandbox Code Playgroud)原始类型(例如,typedef int X)不会发生这种情况.
如果原因涉及构造函数中可能存在副作用的事实,则问题变为:为什么我会得到另外两个警告?
第一个调用用户定义的构造函数来执行直接初始化.这可能会产生副作用.因此x1被使用.(或者至少它不能轻易被确定为未使用.)
第二个复制构造.它调用用户定义的构造函数来创建临时的.然后它调用默认的复制构造函数来构造x2.(有关更详细的讨论和改进,请参阅下面的编辑.)因此x2可以确定它是未使用的,因为它是由编译器生成的构造函数创建的,没有副作用,并且x2本身不会出现在其他地方.
第三个就像第二个.临时是通过用户提供的构造函数创建的,然后复制.临时使用,但x3本身不是.
请参阅此问题的已接受答案:复制初始化和直接初始化之间的C++是否存在差异?
编辑
根据评论,这里有一些进一步的讨论.
首先,有评论认为该警告具有误导性.这可能有些主观,但我要指出,此类警告通常仅在"尽力而为"的基础上提供.如果编译器只挖掘足够深入的代码来检查,那么在特定情况下可能会出现一般情况下无法保证的事情.但是,在某些时候,编译器开发人员必须绘制一条线.这意味着您通常不能指望这样的警告来捕获未使用变量的每个案例.(在另一方面,如果你有一个变量被使用,你会得到一个警告,那将是一个错误.)我个人的感觉是,行为证明了这里是不是误导,但同样,我看到有一些个人在打这样的电话时的解释.
其次,@FrançoisAndrieux指出,如果您修改OP给出的示例以包含用户定义的复制构造函数,您仍会得到相同的警告.这使我对上面的解释产生了疑问,它引用了默认的复制构造函数.这触及了理论的第二点,我将跟进这个案例的具体答案.理论的观点是,除非你真的想深入研究编译器本身并获得其特定的规则,否则手头的问题是编译器是否可以合理地知道变量未使用,请记住可能存在多个这样,编译器可能已经达到了它的结论.
正如最初发布的示例,我认为我的答案提供了一种方式,即编译器可以得出结论.另一种方式,似乎适用于原始形式和评论建议的修改形式是基于复制省略.这里有一个广泛的讨论:什么是复制省略和返回值优化?当前讨论的关键点是,特别是对于复制构造函数,允许编译器忽略副作用.实际上,在某些情况下,编译器需要忽略该副本.显然,这里的编译器在发出警告时直接或间接地考虑了这一点.这也可能是由OP提出的,即所有三种情况下的汇编代码都是相同的 - 这是因为复制操作已经过优化.
让我现在尝试预测下一个问题:"如果复制结构被删除,为什么这三个案例真的不一样?" 我认为,这里的答案是未使用的特定变量.它是构造的第二和第三种情况中的未命名的临时变量,而不是命名变量x2和x3.临时变量与示例中的第一种情况属于相同的模式并且被"使用"(或者至少不能容易地确定为不被使用).一旦复制构造被优化,这仍然留下x2并且x3未被任何标准使用.