In this Tweet, @willkirkby posts:
+!!””
that evaluates as 1 in C/C++, but no, JavaScript is the weird language
C++ is indeed weird, or at least it’s very weakly typed. Let’s go through all the details of what’s going on here:
+!!""
Summary:
Starting from the right, ""
is a string literal, which gets converted to a pointer, which again gets converted to a bool
with the value true
. This then gets passed to two operator!
s, which flip it to false
and back to true
again. Finally, operator+
converts the bool
true
to the int
1
.
All of this happens behind our backs, so to speak, as C++ is very eager to do conversions we didn’t explicitly ask it to do. This eagerness to do implicit conversions is by the way why you should always mark your single argument constructors and your converting operators explicit
.
Detailed explanation:
Now let’s go through this in detail, quoting the C++17 standard. Starting from the right:
""
is a string literal. [lex.string]¶8:
A narrow string literal has type “array of n
const char
”
Then comes operator!
. We have an array of n const char
, can we use that for operator!
? [expr.unary.op]¶9 says:
The operand of the logical negation operator
!
is contextually converted tobool
(Clause 7); its value istrue
if the converted operand isfalse
andfalse
otherwise. The type of the result isbool
.
So we need to contextually convert our array of n const char
to bool
. [conv]¶5 says:
Certain language constructs require that an expression be converted to a Boolean value. An expression
e
appearing in such a context is said to be contextually converted tobool
and is well-formed if and only if the declarationbool t(e);
is well-formed, for some invented temporary variablet
.
So let’s see where bool t(e);
takes us when e
is an array of n const char
.[conv]¶2:
expressions with a given type will be implicitly converted to other types in several contexts:
- When used as the source expression for an initialization
In our case, the expression is of type “array of n const char
“, and we need a bool
. We’re going to need a standard conversion sequence. [conv]¶1:
A standard conversion sequence is a sequence of standard conversions in the following order:
- (1.1) Zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion, and function-to-pointer conversion.
- (1.2) Zero or one conversion from the following set: integral promotions, floating-point promotion, integral conversions, floating-point conversions, floating-integral conversions, pointer conversions, pointer to member conversions, and boolean conversions.— […]
So we can first use an array-to-pointer conversion (1.1) to get from “array of n const char
” to a pointer. We can then use a boolean conversion (1.2) to get from pointer to bool
.
First, the array-to-pointer conversion, [conv.array]¶1:
An lvalue or rvalue of type “array of N T” […] can be converted to a prvalue of type “pointer to T”. The result is a pointer to the first element of the array.
So we now have a pointer to the first element (the terminating \0
).
And then the boolean conversion [conv.bool]¶1:
A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true.
Since our pointer is not a null pointer, its value converts to true
.
Negating this twice with two operator!
s is trivial, we end up back at true
.
Finally, true
is passed to operator+
. [expr.unary.op]¶7:
The operand of the unary
+
operator shall have arithmetic, unscoped enumeration, or pointer type and the result is the value of the argument.
bool
is not an arithmetic type, so we need to promote the bool
true
to an arithmetic type before passing it to opreator+
. [conv.prom]¶6:
A prvalue of type
bool
can be converted to a prvalue of typeint
, withfalse
becoming zero andtrue
becoming one.
So the bool
true
becomes the int
1
, and we’re done.
If you enjoyed this post, you can subscribe to my blog, or follow me on Twitter.
Just wow
Meanwhile, in JavaScript, +!!!”” evaluates to 1, so yes. Yes JavaScript is still the weird language.
I guess it converts the empty string to false? One could argue that that makes more sense than converting it to true via a pointer.
From a high level perspective (say from that of JavaScript) it absolutely makes more sense. But from a lower level perspective (say C++) interpreting it as true makes more sense, as the pointer isn’t 0, and this should be interpreted as true. It all depends on your perspective, I just wanted to point out that all languages have their confusing little quirks.
Yeah, given C++’s C legacy, it makes sense. But if we re-designed C++ from scratch today, I’m not sure we’d end up with the same answer.
JS weirdness here is that the conversion will happen during runtime, unlike the C++
so, 1st – performance, then – you will realize this after “happy” customer’s reports
What exactly do you mean? Conversions happen at run-time for C++ as well, even if they’re statically checked. And since the code in this example compiles just fine, you won’t get any compiler warnings or errors. If this conversion is in some way problematic for you, you won’t discover in until run-time, just like for JavaScript.