I have a quick question about the following expression:
int a_variable = 0;
if(0!=a_variable)
a_variable=1;
what is the difference between "(0 != a_variable)" and "(a_variable != 0)" ?
I dont have any errors for now but is this a wrong way to use it??
if you forget the !, the first will give an error (0 = a_variable) and the second will wreak havoc (a_variable = 0).
Also, with user-defined operators the second form can be implemented with a member function while the first can only be a non-member (possibly friend) function. And it's possible, although a REALLY bad idea, to define the two forms in different ways. Of course since a_variable is an int then there are no user-defined operators in effect in this example.
There is no difference between 0 != x and x != 0.
Any difference it may make is the order in which the arguments will be evaluated. a != b would conventionally evaluate a, then evaluate b and compare them, while b != a would do it the other way round. However, I heard somewhere that the order of evaluation is undefined in some cases.
It doesn't make a big difference with variables or numbers (unless the variable is a class with overloaded != operator), but it may make a difference when you're comparing results of some function calls.
Consider
int x = 1;
int f() {
x = -1;
return x;
}
int g() {
return x;
}
Assuming the operands are evaluated from left to right, then calling (f() != g()) would yield false, because f() will evalute to -1 and g() to -1 - while (g() != f()) would yield true, because g() will evaluate to 1 and f() - to -1.
This is just an example - better avoid writing such code in real life!
Related
Consider the following code:
struct S{
int a, b;
S() : a(0), b(10) {}
};
S operator + (const S& v, int n){
S r = v;
r.a += n;
return r;
}
S operator - (const S& v, int n){
S r = v;
r.b -= n;
return r;
}
S v = S() + 1 - 1;
Is it possible that S() + 1 - 1 will be optimized to S() ?
How does a compiler even determines when this kind of things can be optimized?
No, a compiler is not allowed to optimize
S() + 1 - 1
into
S()
because those two expressions are not equivalent for your class S.
Even though it might be non-idiomatic, it's completely valid for S to be written as you have done, in a way that operator+ and operator- don't cancel each other out (in your case, by modifying different member variables in the 2 operators).
The compiler is required to evaluate S() + 1 before using that result in the evaluation of - 1, and is only allowed to optimize that expression if it can prove that the resulting expression would be the same. In other words, the program must follow the as-if rule. Obviously, in this case the results would be different, and so the optimization is not allowed.
By the same token, if the compiler can see that an expression, and this can be any expression at all, is equivalent to some other expression, then it is allowed to optimize it as much as it wants. But again, the compiler must adhere to the as-if rule, i.e. the resulting program must behave exactly as if none of those optimizations were ever made at all.
Note that while your implementation of S is valid according to the language rules, it is quite strange, and will surprise users of the class. I suggest only doing that for types that are very clearly domain specific, and where users of the type naturally expect that + and - are not necessarily inverses of each other.
To avoid confusion, let's write it like this:
S a;
S b = a + 1 - 1;
To better see what happens in the last line you can rewrite the operator calls explicitly:
S b = a.operator+( 1 ).operator-( 1 );
Already here we see that the compiler cannot simply skip +1 -1. To do that it would have to first prove that applying +1 and then -1 to a S is the same as not applying any operation.
Usually that is the case, since typically + and - are inverses. However, your operators are not inverses of each other, as they increment/decrement different members.
Is it possible that S() + 1 - 1 will be optimized to S() ?
No, because the result would not be the same.
How does a compiler even determines when this kind of things can be optimized?
This is covered by the so-called as-if rule. If there is no observable difference, then compilers are allowed to transform the code to get an optimization. On the other hand, an optimization may never change observable behavior (there are a few exceptions, but they don't apply here).
I have the following piece of code :
int f(int &x, int c){
c = c - 1;
if (c == 0) return 1;
x = x + 1;
return f(x, c)*x;
}
Now, suppose I call the above function like this :
int p = 5;
std::cout << f(p, p) << std::endl;
The output is 9^4, since x is passed by reference, hence the final value of x should be 9, but when the return statement of the above function is changed to :
return x*f(x, c);
the output is 3024 (6*7*8*9). Why is there a difference in output ? Has it anything to do with the order of evaluation of Operator* ? If we are asked to predict the output of the above piece of code, is it fixed, compiler-dependent or unspecified ?
When you write:
f(x,c)*x
the compiler may choose to retrieve the stored value in x (for the second operand) either before or after calling f. So there are many possible ways that execution could proceed. The compiler does not have to use any consistency in this choice.
To avoid the problem you could write:
auto x_temp = x;
return f(x, c) * x_temp;
Note: It is unspecified behaviour; not undefined behaviour because there is a sequence point before and after any function call (or in C++11 terminology, statements within a function are indeterminately-sequenced with respect to the calling code, not unsequenced).
The cause is that f() function has side effect on its x parameter. The variable passed to this parameter is incremented by the value of the second parameter c when the function returns.
Therefore when you swap the order of the operand, you get different results as x contains different values before and after the function is called.
However, note that behaviour of the code written in such way is undefined as compiler is free to swap evaluation of operand in any order. So it can behave differently on different platforms, compilers or even with different optimization settings. Because of that it's generally necessary to avoid such side effects. For details see http://en.cppreference.com/w/c/language/eval_order
Today I found a syntax thing I had never seen before. Are || and , the same?
bool f() {
cout << "f";
return true;
}
bool g() {
cout << "g";
return true;
}
int main() {
if(f(),g())
{
cout<<"OK with ,";
}
cout<<endl;
if(f()||g())
{
cout<<"OK with ||";
}
return 0;
}
From the output of this program, it seems so:
fgOK with ,
fOK with ||
Are they the same exact thing or are there any little differences?
f(),g() means evaluate f() then evaluate g() and return g() since your g() returns true it is that what is returned to if
So, no, the operators are totally different.
You can see the difference if you modify your function to return false from g(). The condition f()||g() will still evaluate to true while f(),g() will return false.
This (comma operator)
if(f(),g())
will evaluate both f() and g() and return the value of the second operand g()
The logical OR operator
if(f()||g())
will not evaluate g() if f() evaluates to true. This is known as short-circuit evaluation - if f() returns true, then the logical OR condition is already satisfied - hence there is no point evaluating g().
So they are NOT the same at all even though under certain conditions you may see the same overall behaviour.
|| is the logical OR operator and by standard it follows short-circuit evaluation (i.e. it won't evaluate the second operand if the first already suffices to determine the entire logical expression)
, is the comma operator that evaluates both but just return the value of the second operand.
By the way you should be seeing something like:
fgOK with ,
fOK with ||
the reason why you're not seeing it might be because you're using a compiler that doesn't strictly follow the standards (I suspect MSVC..)
http://ideone.com/8dSFiY
They are completely different operators that serve completely different purposes.
The main difference is that:
The , operator (unless overloaded) will evaluate all of its arguments and return the last one, no matter what.
The || operator will evaluate all the arguments until it reaches the first trueish value and will not evaluate the rest.
This is also the reason why the output, you are claiming to receive, is wrong.
I am reading a book about C++ and more precisely about the operator overloading.
The example is the following:
const Array &Array::operator=(const Array &right)
{
// check self-assignment
// if not self- assignment do the copying
return *this; //enables x=y=z
}
The explanation provided by the book about returning const ref instead of ref is to avoid assignments such as (x=y)=z. I don't understand why we should avoid this. I understand that x=y is evaluated first in this example and since it returns a const reference the =z part cannot be executed. But why?
(x=y) means x.operator=(y), which returns the object x. Therefore, (x=y)=z means (x.operator=(y)).operator=(z). The expression in parens sets x to y and returns x, and then the outer bit sets x to z. It does not set y to z as you might expect, and as the expression x = y = z does.
This behavior is counter-intuitive (they should all be equal after the assignment, right?); returning a const reference makes it impossible and avoids the problem.
There is no need to avoid this, unless the book is aimed at programmers that commonly write (x=y)=z when they mean x=y=z. In practice, nobody in their right mind writes that, so the precaution is entirely unnecessary. It also forbids some other terse constructs, such as (x=y).nonConstMember(), that hardly anyone writes but that might be useful in some contexts (although they shouldn't be over-used).
#ybungalobill is right, get a better book.
As far as I know, assignment operators do not return const references in idiomatic C++. The Standard types do not return const references either.
std::string a, b, c;
(a = b).clear(); // no objection from compiler
All of my custom assignment operators have returned a non-const reference.
When in doubt, check the Standard library. It's not flawless, but it definitely gets basic things like this correct.
I would look at the behavior of the built-in types.
When defining your own types it is preferable that the operators behave the same way as the built-in types. This allows easy adoption of your classes without having to dig into your code to see why they behave differently from expected.
So if we look at integers:
int main()
{
int x = 5;
int y = 6;
int z = 7;
(x = y) = z;
std::cout << x << " " << y << " " << z << "\n";
}
This works with y unchanged and x being assigned 7. In your code i would expect your assignment operator to work the same way. The standard assignment operator definition:
Array& Array::operator=(Array const& rhs)
{
/* STUFF */
return *this;
}
Should do that just fine (assuming /* STUFF */ is correct).
The only reason I can see is that this book was written to explain C++ to C programmers (or by an author whose C understanding is better than C++ understanding). Because for a C programmer, the expression (x = y) = z is invalid for built-in types, and he probably will to try to get the same behavior with its user-defined types.
However, C and C++ are different languages, and in C++ the expression (x = y) = z is valid even for built-in types. So if you want to have the same behavior for your user-defined types, you should return a non-const reference in the operator =.
I would advise you to get a better book, one that does not make the confusion between C and C++. They are not the same langages, even if they derive from a common base.
So can someone help me grasp all the (or most of the relevant) situations of an assignment inside something like an if(...) or while(...), etc?
What I mean is like:
if(a = b)
or
while(a = &c)
{
}
etc...
When will it evaluate as true, and when will it evaluate as false? Does this change at all depending on the types used in the assignment? What about when there are pointers involved?
Thanks.
In C++ an attribution evaluates to the value being attributed:
int c = 5; // evaluates to 5, as you can see if you print it out
float pi = CalculatePi(); // evaluates to the result
// of the call to the CalculatePi function
So, you statements:
if (a = b) { }
while (a = &c) { }
are roughly equivalent to:
a = b
if (b) { }
a = &c
while (&c) { }
which are the same as
a = b
if (a) { }
a = &c
while (a) { }
And what about those if (a) etc when they are not booleans? Well, if they are integers, 0 is false, the rest is true. This (one "zero" value -> false, the rest -> true) usually holds, but you should really refer to a C++ reference to be sure (however note that writting if (a == 0) is not much more difficult than if (!a), being much simpler to the reader).
Anyways, you should always avoid side-effects that obscure your code.
You should never need to do if (a = b): you can achieve exactly the same thing in other ways that are more clear and that won't look like a mistake (if I read a code like if (a = b) the first thing that comes to my mind is that the developper who wrote that made a mistake; the second, if I triple-check that it is correct, is that I hate him! :-)
Good luck
An assignment "operation" also returns a value. It is the type and value of the expression. If handled by an if type statement:
while (expr)
do ... until (expr)
if (expr)
or the ternary operator (expr) ? (true value) : false value
expr is evaluated. If it is nonzero, it is true. If zero, it is false.
The return type of the assignment is the left hand value, it's what allows statements like a = b = c to compile. In your example:
while(a = &c)
{
}
Returns true when "a" is true, after it has been assigned the value of &c.
In both examples you listed, inside the parentheses is evaluated to true if a is non-zero after the assignment.
In a more common case, you compare a variable with a constant, to avoid this problem, some coding standards require that you write the constant first.
if (A_CONSTANT = var_a)
this would be caught by the compiler, whereas,
if (var_a = A_CONSTANT)
won't.
An assignment statement evaluates to the new value of the variable assigned to (barring bizarre overloads of operator=). If the assignment happens in a boolean context it will then depend on the type of that value how it is treated. If the value is a bool, it is of course treated as a bool. If the value is a numeric value, a non-zero value is treated as true. If the value is a pointer, a non-NULL value is treated as true. If it is a object, the compiler will attempt to convert it to a boolean value (e.g. operator bool). If that is not possible, the compiler will attempt to convert the object to a value that is convertible to bool (e.g. a pointer type, or a numeric type such as int). Finally, if there is no conversion to be performed, or there are multiple possible conversions (e.g. the object defines operator int and operator foo*), the code will fail to compile.
if (a = b)
...
is the shorthand for:
a = b;
if (a != 0)
...
In a while statement, assignment within the condition enables the DRY principle:
while (a = b)
{
...
}
is shorthand for (notice how I had to replicate the assignment so that it is done right before the condition check):
a = b;
while (a != 0)
{
...
a = b;
}
That said, one classic issue with code like this is knowing whether the code intended to do an assignment or if the code forget an '=' when the intent was to write '==' (i.e. should that have been while (a == b).
Because of this, you should never write just a plain assignment (gcc will issue a warning such as "suggest parentheses around assignment used as truth value"). If you want to use assignment in a control structure, you should always surround it with extra parentheses and explicitly add the not-equal:
if ((a = b) != 0)
...
while ((a = b) != 0)
...