预期"警告:未使用的变量"但在C++中没有生成任何内容

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)不会发生这种情况.

  • 如果原因涉及构造函数中可能存在副作用的事实,则问题变为:为什么我会得到另外两个警告?


编辑:

  • 这不是IMO 这个问题的重复,因为在这种情况下构造函数是非平凡的.

Bri*_*ick 8

第一个调用用户定义的构造函数来执行直接初始化.这可能会产生副作用.因此x1被使用.(或者至少它不能轻易被确定为未使用.)

第二个复制构造.它调用用户定义的构造函数来创建临时的.然后它调用默认的复制构造函数来构造x2.(有关更详细的讨论和改进,请参阅下面的编辑.)因此x2可以确定它是未使用的,因为它是由编译器生成的构造函数创建的,没有副作用,并且x2本身不会出现在其他地方.

第三个就像第二个.临时是通过用户提供的构造函数创建的,然后复制.临时使用,但x3本身不是.

请参阅此问题的已接受答案:复制初始化和直接初始化之间的C++是否存在差异?

编辑

根据评论,这里有一些进一步的讨论.

首先,有评论认为该警告具有误导性.这可能有些主观,但我要指出,此类警告通常仅在"尽力而为"的基础上提供.如果编译器只挖掘足够深入的代码来检查,那么在特定情况下可能会出现一般情况下无法保证的事情.但是,在某些时候,编译器开发人员必须绘制一条线.这意味着您通常不能指望这样的警告来捕获未使用变量的每个案例.(在另一方面,如果你有一个变量使用,你会得到一个警告,那将是一个错误.)我个人的感觉是,行为证明了这里是不是误导,但同样,我看到有一些个人在打这样的电话时的解释.

其次,@FrançoisAndrieux指出,如果您修改OP给出的示例以包含用户定义的复制构造函数,您仍会得到相同的警告.这使我对上面的解释产生了疑问,它引用了默认的复制构造函数.这触及了理论的第二点,我将跟进这个案例的具体答案.理论的观点是,除非你真的想深入研究编译器本身并获得其特定的规则,否则手头的问题是编译器是否可以合理地知道变量未使用,请记住可能存在多个这样,编译器可能已经达到了它的结论.

正如最初发布的示例,我认为我的答案提供了一种方式,即编译器可以得出结论.另一种方式,似乎适用于原始形式和评论建议的修改形式是基于复制省略.这里有一个广泛的讨论:什么是复制省略和返回值优化?当前讨论的关键点是,特别是对于复制构造函数,允许编译器忽略副作用.实际上,在某些情况下,编译器需要忽略该副本.显然,这里的编译器在发出警告时直接或间接地考虑了这一点.这也可能是由OP提出的,即所有三种情况下的汇编代码都是相同的 - 这是因为复制操作已经过优化.

让我现在尝试预测下一个问题:"如果复制结构被删除,为什么这三个案例真的不一样?" 我认为,这里的答案是未使用的特定变量.它是构造的第二和第三种情况中的未命名的临时变量,而不是命名变量x2x3.临时变量与示例中的第一种情况属于相同的模式并且被"使用"(或者至少不能容易地确定为不被使用).一旦复制构造被优化,这仍然留下x2并且x3未被任何标准使用.