struct X {
X (int x) {}
};
int main() {
X x1(0); // nothing!
X x2 = 0; // warning: unused variable
X x3 = X(0); // warning: unused variable
}
Why no warning is generated for x1
?
I'm compiling with the -Wall
option.
Both GCC and Clang produce equivalent output.
Those 3 lines generate the same assembler instructions (placed some asm("nop")
for clarity):
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
It does not happen with primitive types (e.g., typedef int X
).
If the reason concerns the fact that there may be side effects in the constructor then the question becomes: why do I get the other two warnings?
The first one calls your user-defined constructor to perform direct initialization. This could have side-effects. Hence x1
is used. (Or at least it cannot easily enough be determined to be unused.)
The second one copy constructs. It calls your user-defined constructor to create a temporary. It then calls the default copy constructor to construct x2
. (See below in edit for more detailed discussion and refinement.) Hence x2
can be determined to be unused because it was created by a compiler-generated constructor with no side-effects, and x2
itself does not appear anywhere else.
The third is like the second. A temporary is created via your user-provided constructor, and then copied. The temporary is used, but x3
itself is not.
See the accepted answer to this question: Is there a difference in C++ between copy initialization and direct initialization?
EDIT
Based on the comments, here's some further discussion.
First, there was the comment that the warning is misleading. This is perhaps somewhat subjective, but I would point out that warnings of this sort are often provided on a "best effort" basis only. It could be that something that is not guaranteed in general is true in a specific case if the compiler would only dig far enough into the code to check. At some point, though, the compiler developers have to draw a line. That means that you generally cannot count on a warning like this to catch every case of an unused variable. (On the other hand, if you have a variable that is used and you get a warning, that would be a bug.) My personal feeling is that the behavior demonstrated here is not misleading, but, again, I see that there's some personal interpretation in making such a call.
Second, it was pointed out by @FrançoisAndrieux that if you modified the example given by the OP to include a user-defined copy constructor, you still get the same warnings. That calls into question my explanation above, which referenced the default copy constructor. This touches on a second point of theory, which I'll follow up with a specific answer for this case. The point of theory is that, unless you really want to dig into the compiler itself and get its specific rules, the issue at hand is whether the compiler can reasonably know that the variable is unused, keeping in mind that there may be more than one way that the compiler could have reached its conclusion.
As the example was originally posted, I think my answer gives a way that the complier could have come to its conclusion. Another way, that seems to be applicable both to the original form and to the modified form suggested by the comment is based on copy elision. There's an extensive discussion of this here: What are copy elision and return value optimization? The key point for the current discussion is that, specifically for copy constructors, the compiler is allowed to ignore side effects. In fact, in some cases, the compiler is required to ignore the copy. Evidently, the compiler here is taking this into account, either directly or indirectly, when issuing the warning. This also probably goes to the point made by the OP that the assembly code in all three cases is the same - that's because the copy action has been optimized out.
Let me try now to anticipate the next question: "If the copy construct is elided, why aren't the three cases really the same?" The answer here, I think, is in which specific variable are unused. It's the unnamed, temp variables in the second and third case that are constructed, not the named variables x2
and x3
. The temp variables fall into same pattern as the first case in the example and are "used" (or at least cannot be determined easily enough to be unused). That still leaves x2
and x3
unused by any standard once the copy construct is optimized out.
@Brick answer's follows. For different semantics you can end up with same final object code, to prove that try add -fno-elide-constructors
to your flags, elision is the culprit that leads to same object code, even in -O0
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With