No side effect with the comma operator, return operator and nullptr?

I have the following test code:

#include <cstdint> #include <cassert> enum class Result : std::uint32_t {SUCCESS = 0, INSUCCESS = 1}; void* func(Result& result) { // works great /* result = Result::INSUCCESS; return NULL; */ // error: invalid conversion from 'long int' to 'void*' [-fpermissive] /* return result = Result::INSUCCESS, NULL; */ // compiles, but <result> is not set??? return result = Result::INSUCCESS, nullptr; } void testReturnWithSideEffects() { Result result = Result::SUCCESS; func(result); assert(result == Result::INSUCCESS); } 

There are two questions here, but the second one interests me:

Why is the result not set?

Edit: Thank you all for confirming this. The workaround that I decided to use is to replace:

 return result = Result::INSUCCESS, nullptr; 

with the following:

 return result = Result::INSUCCESS, (void*)NULL; 

Additional note: of course, my production scenario is with a different type of pointer (not void *), but I am simplified for illustrative purposes.

One more note: from the workaround, you can say that something suspicious is happening there with this nullptr. I assume that the example line that does not compile should actually compile, and these 2 questions are probably related in some way.

And the third and final remark for those who stated the “deception” or “unreadability” of my code: readability is largely subjective, for example, I can argue that such a transcript can make the code more structured, which can actually help identify defects.

+7
c ++ gcc c ++ 11 nullptr comma
source share
3 answers

Second case:

 return result = Result::INSUCCESS, nullptr; 

looks like a gcc error, it seems to ignore the left side of the comma operator in the context of the return statement, if I change the return to this ( see live ):

 return (printf("hello\n"), nullptr); 

there is no way out, but if we do this outside of the return , we get the expected result. It seems to be fixed in 4.8 , but I can play back with 4.6 and 4.7 .

If we look at the draft C ++ project in section 5.18 The comma operator, it says (emphasis mine):

A pair of expressions separated by a comma is evaluated from left to right; the left expression is an expression with a discarded value (section 5). 83 Each value calculation and side effect associated with the left expression are sequenced before each value calculation and side effect associated with the correct expression . The type and value of the result is the type and value of the correct operand; the result has the same category of values ​​as its right operand, and is a bit field if its right operand is a glvalue and a bit field. If the value of the right operand is temporary (12.2), the result is temporary.

Regardless of how several people have already been mentioned, this code is smart, but hard to read, and therefore it will be difficult to maintain, dividing it into two lines will work fine. I always like to keep this quote from Brian Kernigan in mind (there may be several other versions of this):

Everyone knows that debugging is twice as difficult as writing a program in the first place. So, if you are as smart as you can be when you write, how do you debug it?

In the first case, the error is valid if we look at section 4.10 Transformations of a pointer that says (emphasis mine forward):

The null pointer constant is an integral constant expression (5.19) prvalue of an integer type that evaluates to zero or a value of type std :: nullptr_t. The null pointer constant can be converted to a pointer type; the result is a null pointer value of this type and different from any other pointer value or object function pointer type. This conversion is called null pointer conversion.

expression:

 result = Result::INSUCCESS, NULL 

is not a constant expression, since it contains = , what is a constant expression is considered in section 5.19 Constant expressions and says:

A conditional expression is an expression of a constant constant if it includes one of the following as a potentially evaluated subexpression (3.2) [...]

and includes:

appointment or compound appointment (5.17); or

Using nullptr is fine, since it is a prvalue for std :: nullptr_t, we can see that from section 12.14.7 literal pointer that says:

The pointer literal is the nullptr keyword. This is a value of type std :: nullptr_t . [Note: std :: nullptr_t is a separate type, which is neither a pointer type nor a pointer to a member type; rather, a prvalue of this type is a null pointer constant and can be converted to null a pointer value or a null element pointer value. See 4.10 and 4.11. -endnote]

+12
source share

My test shows that your code should work. I ran this on compileonline.com:

 #include <iostream> using namespace std; int func5() { return 5; } int func(int& result) { return result = func5(), 10; } enum class Result : uint32_t {SUCCESS = 0, INSUCCESS = 1}; void* func(Result& result) { // works great /* result = Result::INSUCCESS; return NULL; */ // error: invalid conversion from 'long int' to 'void*' [-fpermissive] /* return result = Result::INSUCCESS, NULL; */ // compiles, but <result> is not set??? return result = Result::INSUCCESS, nullptr; } int main() { int b = 0; int a = func(b); cout << a << ", " << b << endl; Result result = Result::SUCCESS; func(result); cout << uint32_t(result) << endl; } 

Result:

 Compiling the source code.... $g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1 Executing the program.... $demo 10, 5 1 

Perhaps you have a bug in your compiler?

0
source share

A quote from the C ++ standard is the comma operator:

The type and value of the result is the type and value of the correct operand

so you get "nullptr", which translates to the result, as well as SUCCESS.

-2
source share

All Articles