Shouldn't the Postfix Operators be Considered Binary Operators - c++

The postfix operators take an int parameter. There is already a question as to why, and it seems like the answer is: "Because Bjarne Stroustrup said so"
I'm uncomfortable with that answer. If Bjarne Stroustrup needed something to tip the compiler off to behave differently, why couldn't he just key off whether the operator returned a reference? It leaves me questioning:
Why can't I do: foo++ 13;
Why isn't the int parameter defaulted to 1
Why is this considered a unary operator at all, it takes an argument

If Bjarne Stroustrup needed something to tip the compiler off to behave differently, why couldn't he just key off whether the operator returned a reference?
Because you cannot overload a function based on its return type. It is the parameters, the const qualification and reference qualification that the function can be overloaded for
Why can't I do: foo++ 13;
Because the (int) parameter is just there for overload resolution. You do not take it in or use the parameter.
Why isn't the int parameter defaulted to 1
Again it is not used. It is just there to tell the compiler if it is the prefix or postfix version.
Why is this considered a unary operator at all, it takes an argument
It actually doesn't take an argument. The parameter is only there to make them different. It only affects and works on one operand so it is a unary operator.

To answer the question in the title: in foo++, ++ is clearly a unary operator. The fact that its implementation could look like a binary operator if you squint at it just right doesn't change how it's used, and that's what makes it unary.

Related

Why are multiple pre-increments allowed in C++ but not in C? [duplicate]

This question already has answers here:
Why are multiple increments/decrements valid in C++ but not in C?
(4 answers)
Closed 5 years ago.
Why is
int main()
{
int i = 0;
++++i;
}
valid C++ but not valid C?
C and C++ say different things about the result of prefix ++. In C++:
[expr.pre.incr]
The operand of prefix ++ is modified by adding 1. The operand shall be
a modifiable lvalue. The type of the operand shall be an arithmetic
type other than cv bool, or a pointer to a completely-defined object
type. The result is the updated operand; it is an lvalue, and it is a
bit-field if the operand is a bit-field. The expression ++x is
equivalent to x+=1.
So ++ can be applied on the result again, because the result is basically just the object being incremented and is an lvalue. In C however:
6.5.3 Unary operators
The operand of the prefix increment or decrement operator shall have atomic, qualified, or unqualified real or pointer type, and shall be a modifiable lvalue.
The value of the operand of the prefix ++ operator is incremented. The
result is the new value of the operand after incrementation.
The result is not an lvalue; it's just the pure value of the incrementation. So you can't apply any operator that requires an lvalue on it, including ++.
If you are ever told the C++ and C are superset or subset of each other, know that it is not the case. There are many differences that make that assertion false.
In C, it's always been that way. Possibly because pre-incremented ++ can be optimised to a single machine code instruction on many CPUs, including ones from the 1970s which was when the ++ concept developed.
In C++ though there's the symmetry with operator overloading to consider. To match C, the canonical pre-increment ++ would need to return const &, unless you had different behaviour for user-defined and built-in types (which would be a smell). Restricting the return to const & is a contrivance. So the return of ++ gets relaxed from the C rules, at the expense of increased compiler complexity in order to exploit any CPU optimisations for built-in types.
I assume you understand why it's fine in C++ so I'm not going to elaborate on that.
For whatever it's worth, here's my test result:
t.c:6:2: error: lvalue required as increment operand
++ ++c;
^
Regarding CppReference:
Non-lvalue object expressions
Colloquially known as rvalues, non-lvalue object expressions are the expressions of object types that do not designate objects, but rather values that have no object identity or storage location. The address of a non-lvalue object expression cannot be taken.
The following expressions are non-lvalue object expressions:
all operators not specified to return lvalues, including
increment and decrement operators (note: pre- forms are lvalues in C++)
And Section 6.5.3.1 from n1570:
The value of the operand of the prefix ++ operator is incremented. The result is the new value of the operand after incrementation.
So in C, the result of prefix increment and prefix decrement operators are not required to be lvalue, thus not incrementable again. In fact, such word can be understood as "required to be rvalue".
The other answers explain the way that the standards diverge in what they require. This answer provides a motivating example in the area of difference.
In C++, you can have a function like int& foo(int&);, which has no analog in C. It is useful (and not onerous) for C++ to have the option of foo(foo(x));.
Imagine for a moment that operations on basic types were defined somewhere, e.g. int& operator++(int&);. ++++x itself is not a motivating example, but it fits the pattern of foo above.

Compiles as C++ but not C (error: lvalue required as unary '&' operand)

This line compiles when I use C++, but not C:
gmtime(&(*(time_t *)alloca(sizeof(time_t)) = time(NULL))); //make an lvalue with alloca
I'm surprised by this difference. There is not even a warning for C++.
When I specify gcc -x c, the message is:
playground.cpp:25:8: error: lvalue required as unary '&' operand
gmtime(&(*(time_t *)alloca(sizeof(time_t)) = time(NULL)));
^
Isn't the & here just an address-of operator? Why is it different in C and C++?
Although I can use compound literals in C, still is it possible to modify my syntax to make it work in both C & C++?
In C11 6.5.16/3:
An assignment operator stores a value in the object designated by the left operand. An assignment expression has the value of the left operand after the assignment, but is not an lvalue.
In C++14 5.17/1:
The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand and return an lvalue referring to the left operand.
(Earlier versions of the language standards in each case specified the same thing).
Since the address-of operator can only operate on an lvalue, the code is correct in C++ but not in C.
Regarding the question "Is it possible to modify my syntax to make it work in both C & C++?". This is not a desirable goal; the two languages are different and you should decide what you're writing. This makes about as much sense as trying to stick to syntax that works in both C and Java.
As suggested by others, you could write:
time_t t = time(NULL);
gmtime(&t);
which has the benefits over your original code of being:
simpler, therefore easier to understand and maintain
does not depend on non-standard alloca function
does not have potential alignment violation
uses no more memory and perhaps uses less

Low level details of C/C++ assignment operator implementation. What does it return?

I m a total newbie to a C++ world (and C too). And don't know all its details. But one thing really bothers me.
It is constructions like :
while (a=b) {...} .As I understand this magic works because assignment operator in C and C++ returns something.
So the questions: what does it return? Is this a documented thing?Does it work the same in C and C++. Low level details about assignment operator and its implementation in both C and C++ (if there is a difference) will be very appreciated!
I hope that this question won't be closed, because I can't find a comprehensive explanation and good material on this theme from the low level point of view all the more so.
For built-in types in C++ evaluating an assignment expression produces an lvalue that is the left hand side of the assignment expression. The assignment is sequenced before the result can be used, so when the result is converted to an rvalue you get the newly assigned value:
int a, b=5;
int &c = (a=b);
assert(&c==&a);
b=10;
assert(10==(a=b));
C is almost but not exactly the same. The result of an assignment expression in C is an rvalue the same as the value newly assigned to the left hand side of the assignment.
int *c = &(a=b); // not legal in C because you can only take the address of lvalues.
Usually if the result of an assignment is used at all it's used as an rvalue (e.g., a=b=c), so this difference between C++ and C largely goes unnoticed.
The assignment operator is defined (in C) as returning the value of the variable that was assigned to - i.e. the value of the expression (a=b) is the value of a after the expression has been evaluated.
It can be defined to be something different (of the same type) for user-defined operator overloads in C++, but I suspect most would consider this to be a very unpleasant use of operator overloading.
You can use this (non-boolean) value in a while (or an if, etc.) because of type conversion - using a value in a conditional context causes it to be implicitly converted to something which makes sense in a conditional context. In C++, this is bool, and you can define your own conversion (for your own type) by overloading operator bool(). In C, anything other than 0 is true.
To understand such expressions, you have to first understand that, positive integers are considered as 'true' and 0 is considered as false.
An assignment evaluates to the left hand side of the operator = as its value. So, while(a=b) { } would mean, while(1 /*true*/) if a after being assigned to b evaluates to non-zero. Else, it is considered as while(0 /*false*/)
Similarly, with the operator (a=b)?1:0 is the value of a after being assigned to b .. if it is non-zero then the value is taken as true and the statement following ? will be executed, or the statement following : is executed.
Assignments usually evaluate to the value at the left hand side of the operator = where as, logical operators(such as ==, && etc) evaluate to 1 or 0.
Note: with C++, it will depend upon if or not, a certain operator is overloaded.. and it will also depend upon the return type of the overloaded operator.
The assignment operators in C and C++ return the value of the variable being assigned to, i.e., their left operand. In your example of a = b, the value of this entire expression is the value that is assigned to a (which is the value of b converted into the type of a).
So you can say that the assignment operator "returns" the value of its left operand.
In C++ it's a little more complicated because you can overload the = operator with an actual user-defined function, and have it return something other than the value (and type) of the left operand.

C++ operator overloading takes pointer type as parameter?

I'm new to C++ and trying to figure out the differences between pointer and reference. I've just read this short summary.
In the article, the author mentioned that day *operator++ (day *d); won't compile (note: day is an enum type) and argued that the parameter for this overloaded operator function must be type T, T&, or T const&, where T is a class or enum type.
I assume that pointer is a built-in type rather than a class or enum so it can't be used to overload operators and that operator overloading is not possible for all built-in types such as int and double.
For example, int i = 1; ++i; would never result in i being 3 by overloading the ++ operator for the type int.
Am I correct? Please help me understand this problem better.
First rule in Operator overloading is:
You cannot overload operators for built-in data types, You can only for your custom data types, So you are correct in that regard.
Yes, pointer are primitive types and not objects. They are just numbers (the memory address of the object they point to), and as such arithmetics can be applied to them.
Yes, you cannot overload operators for primitive types (you can however overload binary operators in a class that take a primitive type parameter).

Are assignment operators "required" to return?

According to the C++ standard, can I be sure that assignment operators for built-in variables return (the original value)?
Or is this implementation dependent (yet simply have most popular compilers implemented this)?
Yes, it is guaranteed:
5.17 Assignment and compound assignment operators
The assignment operator (=) and the compound assignment operators all group
right-to-left. All require a modifiable lvalue as their left operand
and return an lvalue referring to the left operand.
This applies to built-in types. With user-defined types it can return anything.
It depends on what you mean by "the original value".
For example:
#include <iostream>
int main() {
int i;
std::cout << (i = 1.9) << "\n";
}
prints 1. The assignment expression yields the new value of the LHS (namely 1), not the "original value" of the RHS (1.9).
I'm not sure whether that's what you meant to ask about.
According to the C++ standard, can I be sure that assignment operators for build in variables return (the original value)?
Edit I get it now, I think. Yes: you can be sure that the built-in types return the original value by reference after operator=, *=, /=, -=, +=, ^=, ~=, &= and |=.
"For build in variables" is a bit cryptic to me. However,
X makeX()
{
return X();
} // must have accessible copy constructor
// ...
X x;
x = makeX(); // ... _and_ assignment operator
Or is this implementation dependent
It should not be (edit see standard reference by UncleBens)
Yes. It is guaranteed that all assignments and augmented assignments on predefined types work that way.
Note however that user defined types assignments or augmented assignments can instead for example return void. This is not good practice and shouldn't be done (it raises the surprise effect on users - not a good thing), but it's technically possible so if you are writing for example a template library you should not make that assumption unless it's really important for you.
Yes. Operator semantics will simply not work otherwise.