All C++ operators that I have worked with return something, for example the + operator returns the result of the addition.
Do all C++ operators return something, or are there some C++ operators that do not return anything?
No, not all operators return something.
Although they are probably not exactly what you are thinking about, note that the delete and delete[] C++ 'keywords' are actually operators; and they are defined as having the void return type - which means they evaluate to nothing (which is not 'something').
From cppreference:
void operator delete ( void* ptr ) noexcept;
void operator delete[]( void* ptr ) noexcept;
Operators of custom types can be overloaded to do the most weirdest things.
for example the + operator returns the result of the addition.
Not necessarily:
#include <iostream>
struct foo {
int value = 0;
void operator+(int x) {
value += x;
}
};
int main () {
foo f;
f + 3;
}
Here operator+ adds the left hand side to the value member, and its return type is void. This is a made-up example, but, in general, not returning something from a custom operator is not unusual.
The only operator that can be overloaded and that has the requirement of returning something, that I am aware of, is operator->. It must either return a raw pointer or an object that has an operator->.
To nitpick, operators don't return anything. They are just lexical elements that we use to create expressions in the language. Now, expressions have types and may evaluate to values, and I assume this is what you mean by operators "returning things".
And, well, yes. There are C++ expressions with type void (and consequentially don't evaluate to any value). Some are obvious, others less so. A nice example would be
throw std::runtime_error()
throw is an expression under the C++ grammar. You can use it in other expressions, for instance in the conditional expression
return goodStatus() ? getValue() : throw std::runtime_error();
And the type of a throw expression, is void. Obviously since this just causes execution to rapidly go elsewhere, the expression has no value.
None of the built-in C++ operators return something. Overloaded C++ operators return something insofar that the operator notation is a syntactic sugar for a function call.
Rather, operators all evaluate to something. That something has a well-defined value as well as a type. Even the function call operator void operator()(/*params*/) is a void type.
For example, +'a' is an int type with the value of 'a' encoded on your platform.
If your question is "Can C++ operators have a void return type?" then the answer is most certainly yes.
You can actually define a function call operator to return nothing. For example:
struct Task {
void operator()() const;
};
operator void(): user defined conversion function to void
You may define the peculiar operator void() conversion function, where the compiler will even warn you that the T to void conversion function will never be used:
#include <iostream>
struct Foo {
operator void() { std::cout << "Foo::operator void()!"; }
// warning: conversion function converting 'Foo' to
// 'void' will never be used
};
int main() {
Foo f;
(void)f; // nothing
f.operator void(); // Foo::operator void()!
}
as governed by [class.conv.fct]/1
[...] A conversion function is never used to convert a (possibly
cv-qualified) object to the (possibly cv-qualified) same object
type (or a reference to it), to a (possibly cv-qualified) base class
of that type (or a reference to it), or to (possibly cv-qualified)
void.117
(117)
These conversions are considered as standard conversions for the
purposes of overload resolution ([over.best.ics], [over.ics.ref]) and
therefore initialization ([dcl.init]) and explicit casts. A
conversion to void does not invoke any conversion function
([expr.static.cast]). Even though never directly called to perform a
conversion, such conversion functions can be declared and can
potentially be reached through a call to a virtual conversion function
in a base class.
Whilst, however, as is shown above, you can still invoke it using the explicit .operator void() syntax.
The operators defined (builtin) by the language are tokens used to perform different kinds of computations:
arithmetic (+,-,*,/)
increment/decrement (++,--)
assignment (=,+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=)
logic (!,&&,||)
relational (==,!=,>,<,>=,<=)
conditional ?
comma
and so on. The list can be very extensive.
In language references like this one or this one, these are not necessarily referenced as returning something, just performing an arithmetic or logic operation, a comparison by which means a variable may be modified, etc.
Since this operation results in some value, it may be interpreted as been "returned" by the operator, but it is different from a function return value.
The overloaded operators, on the other hand, can be defined with a return value of some type, even that can be void, so, no, not all operators return some value in C++.
I'm assuming you're talking about operator functions and not operators as a syntactic unit of the language.
If you overload operators on any type, you may actually return whatever you want.
This also makes a lot of sense, because operations like * or () may sometimes very intuitively not return their input type. Imagine multiplying a complex number type with a real number type. Or an operator that returns an element from a collection.
You may also overload the ++ and -- operators to not return anything thus removing the extremely error-prone mixing of side-effect and expression value that the standard version has.
No.
Consider these two examples here:
int multiply (int a, int b) {
return a*b;
}
void multiply_void(int a, int b) {
cout << a*b;
//or cout << multiply(a,b);
}
First function will return an integer value which can be used by another function.
The value is returned and stored in memory to be used when necessary. If not, it is not visible to human & just sits happily in the memory.
Second function will output to the default output device(usually console).
The value returned by the multiplication operator is passed to an output device.
The function multiply_void does not return an actual value.
All operators return something. They are called operators because they operate something , therefore they will return something. Those Operators who do not return something cant be called operators. Either they will return some value or return True or False depending upon the situation.
Operators on their own do not necessarily return anything. Function calls return values. Operators can result in values.
Operators can sometimes invoke functions. Examples include:
• The function call operator.
• An overloaded operator that gets transformed into a function call.
• operator new, which is invoked as part of a new-expression, is a function call.
My only purpose in mentioning function calls is to clarify the result vs. return. Based on looking at all 126 instances of “return” and words derived from it in [expr], the section seems to carefully use the word return to refer to:
• the result of a function call
• aspects of control flow related to coroutines
OK, that’s enough pedantry on result vs. return.
C++ Operators return something or not is depends upon how you used them. Built-In C++ operators return some value until and unless enforced to return void.
Related
I am just starting C++. I am a bit confused about the return type of assignment and dereference operator. I am following the book C++ Primer. At various occasions, the author says that the return type of assignment operator is reference to the type of left hand operand but later on, he says that the return type is the type of the left hand operand. I have referred C++11 Standard Sec. 5.17, where the return type is described as "lvalue referring to left hand operand".
Similarly, I can't figure out whether dereference returns the pointed-to object or the reference to the object.
Are these statements equivalent? If so, then how? Any explanation would be appreciated.
The standard correctly defines the return type of an assignment operator.
Actually, the assignment operation itself doesn't depend on the return value - that's why the return type isn't straightforward to understanding.
The return type is important for chaining operations.
Consider the following construction: a = b = c;. This should be equal to a = (b = c), i.e. c should be assigned into b and b into a. Rewrite this as a.operator=(b.operator=(c)). In order for the assignment into a to work correctly the return type of b.operator=(c) must be reference to the inner assignment result (it will work with copy too but that's just an unnecessary overhead).
The dereference operator return type depends on your inner logic, define it in the way that suits your needs.
They can both be anything, but usually operator = returns the current object by reference, i.e.
A& A::operator = ( ... )
{
return *this;
}
And yes, "reference to the type of left hand operand" and "lvalue referring to left hand operand" mean the same thing.
The dereference operator can have basically any return type. It mostly depends on the logic of the program, because you're overloading the operator that applies to an object, not to a pointer to the object. Usually, this is used for smart pointers, or iterators, and return the object they wrap around:
struct smart_ptr
{
T* innerPtr;
T* smart_ptr::operator* ()
{
return innerPtr;
}
}
smart_ptr p;
T* x = *p;
I have seen similar issues, but I guess it would be best to use
X& X::operator=(const X&);
Using this, you will be able to reuse the object in a chain-assignment.
Given the following class:
struct Object {
int x, y;
void addtoall( int value ){ x += value; y += value; };
Object& addtoall( int value ){ x += value; y += value; return *this; };
};
What is the difference between the two member functions?
I understand that returning a reference to self is required for some operator overloads (e.g: operator+= ), but excluding operator overloading, is it necessary? If not, when would you want or need to return the reference to self as opposed to returning void?
I apologize if this could be found via google-fu, or is a very basic question, but I wasn't sure what exactly to search (and not for lack of trying).
What is the difference between the two member functions?
The function returning a reference to the instance can be chained when called like
Object o;
o.addtoall(5).addtoall(6).addtoall(7);
If this is useful depends on the actual use case, but it's often used to develop so called domain specific language syntax.
I understand that returning a reference to self is required for some operator overloads (e.g: operator+=)
No, it's not. Many people (including myself) advocate that such operators should be declared to return void, most notably operator=, the chaining (or expression reuse) of which actually obscures code in the vast majority of cases it is used. We do it for (unfortunate) convention, only.
What's chaining? Precisely what returning such references is intended to allow:
std::string{"asdf"}.append(c1).append(c2)
append returns string&, unsurprisingly. While mildly useful for string (append({c1, c2}) is more concise), it's pointless in most other cases (such as yours, where we could just add two ints instead of calling the method twice).
Think in a similar fashion like:
1. The bare name of an array is equivalent with the pointer to the first element, without the need to specify index 0.
2. toString() from Java makes it possible to use the name of an object as a string without calling any object method.
Now is there a way in C++ to use the name of a class object to refer to its first member?
Consider:
class Program
{
public:
int id;
char *str;
};
void function(int p)
{
//...
}
and then:
Program prog0;
function(prog0); // instead of function(prog0.id)
Any way to "hide" the member reference?
EDIT:
Why was the holyBlackCat's answer deleted? I was inclining to vote it as the best answer -- no offense, Mateusz. But he was the first to suggest conversion operator and the example was complete and simple.
In C++, such behaviour would be a cataclysm. If I understand correctly, Java tries to convert object of type A to object of type B by searching for first member in A, that is of type B or is implicitly convertible to B.
C++ wasn't designed that way. We like to write code, that is always predictable. You can achieve what you want, but for a price.
The best solution in this case would be conversion operator - consider:
class Program
{
public:
int id;
char *str;
operator int()
{
return this->id;
}
//You can have more than one!
operator const char*()
{
return this->str;
}
};
void function_int(int p)
{
}
void function_str(const char* s)
{
}
Now it is possible to do the following:
Program prog;
function_int(prog); //Equivalent of function_int(prog.id)
function_str(prog); //Equivalent of function_int(prog.str)
The price is, that if you add another int and place it before id it will not be used in conversion, because we stated in our operator explicitly, that "int content" of our class is represented by id and this member is considered when it comes to such conversion.
However, even this simple example shows some potential problems - overloading functions with integral and pointer types could result in very unpredictable behavior. When type contains conversion operators to both pointers and integers, it can get even worse.
Assume, that we have following function:
void func(unsigned long)
{
}
And we call func with argument of type Program. Which conversion operator would you expect to be called? Compiler knows how to convert Program to either int or const char*, but not unsigned long. This article on cppreference should help you to understand how implicit conversions work.
Also, as Barry pointed out, more meaningless constructs become available. Consider this one:
int x = prog + 2
What does it mean? It is perfectly valid code, though. That is why conversion operators should be dosed extremely carefully (in pre-C++11 era, there was a general advise, that every class should have at most one such operator).
Quote from MSDN:
If a conversion is required that causes an ambiguity, an error is generated. Ambiguities arise when more than one user-defined conversion is available or when a user-defined conversion and a built-in conversion exist.
Sometimes, simple solution to this problem is to mark conversion operator with explicit keyword, so you would need to change above calls to:
function_int((int)prog);
function_str((const char*)prog);
It is not as pretty as the previous form, but much safer. It basically means, that compiler is forbidden to perform any implicit conversion using operator marked as explicit. Very useful to avoid ambiguous calls, while still providing some flexibility in code - you can still very easily convert objects of one type to another, but you can be sure when and where these conversions are performed.
However, explicit conversion operators are still not supported by some compilers, as this is C++ 11 feature (for example, Visual C++ 11 doesn't support it).
You can read more about explicit keyword here.
Now is there a way in C++ to use the name of a class object to refer to its first member?
No, C++ doesn't have any reflection, so there's no way to actually determine what the "first member" is.
However, if what you really want is to get an ID for any object, you could just require that object to have that method:
template <typename T>
void function(const T& t) {
int id = t.getID();
// etc.
}
Without knowing more about your use-case, it's hard to know what to propose.
Given code like the following:
void f()
{
int i;
i = 0;
}
is it possible the system could throw an exception due to the simple assignment?
[Edit: For Those saying, "No an exception cannot occur," can You point Me in the direction of the part of the C++ standard which says this? I am having trouble finding it.]
Although you'd probably be hard put to find an assurance of it in the standard, a simple rule of thumb is that anything that's legitimate in C probably can't throw. [Edit: The closest I'm aware of to a direct statement to this effect is at §15/2, which says that:
Code that executes a throw-expression is said to “throw an exception;” [...]
Looking at that in reverse, code that does not execute a throw-expression does not throw an exception.]
Throwing is basically restricted to two possibilities: the first is invoking UB. The second is doing something unique to C++, such as assigning to a user-defined type which overloads operator =, or using a new expression.
Edit: As far as an assignment goes, there are quite a few ways it can throw. Obviously, throwing in the assignment operator itself would do it, but there are a fair number of others. Just for example, if the source type doesn't match the target type, you might get a conversion via a cast operator in the source or a constructor in the target -- either of which might throw.
There's quite a lot of things that look like assignments that can throw one way or another:
int operator"" _t(const char *) { throw 0; } // C++11 user defined literal
struct foo {
foo(int) { throw 0; }
operator int() { throw 0; }
foo& operator=(int) { throw 0; }
};
int main() {
int i;
i = 0; // can't throw
i = 0_t; // User defined literal throws
foo f = 0; // Constructor throws
i = f; // conversion operator throws
f = 0; // assignment throws
f = f; // both conversion and assignment would like to throw
}
(including the new one from C++11)
If you're concerned about assinging 0 (which has type int) to an
int, §5.17 of the standard specifies very exactly the semantics of the
assignment operation, and there is no case where an exception can occur.
If you're concerned about assigning an arbitrary expression to an int,
§5.17 says that “the expression is implicitly converted to the
cv-unqualified type of the left operand." Depending on the actual type
of the right operand:
If it is an integral type, if the actual value cannot be represented in an int, the results are implementation defined (the C standard is more explicit: it must either result in an int with an implementation defined value, or you will get an implementation defined signal)
If it is a floating point value, the result is undefined behavior if the value after truncation to zero cannot be represented in an int, the behavior is undefined (so you might get an exception)
If it is a user defined type, then a user defined conversion operator will be called. Which can throw an exception.
If you're concerned about assigning other types: for each set of
non-class types, there is a list of rules like the above, but the only
possible exceptions will be as a result of a type conversion. For class
types, the operator= will be used. Which can throw an exception,
depending on what is in it.
It can throw an exception only in one case: when overloaded operator=() for those two types throws; similary, when conversion is needed, the converting constructor or operator T() can also throw. It depends on the exact implementation then - to find out if it will throw, look for information about it in documentation of library you are using.
if it's just an int, then no - it will not throw.
if it's something more complex, such as a vector, then it may throw for a number of reasons (e.g. allocation failure or alteration from a secondary thread).
I'm a little confused as to why I've been told to return const foo from a binary operator in c++ instead of just foo.
I've been reading Bruce Eckel's "Thinking in C++", and in the chapter on operator overloading, he says that "by making the return value [of an over-loading binary operator] const, you state that only a const member function can be called for that return value. This is const-correct, because it prevents you from storing potentially valuable information in an object that will be most likely be lost".
However, if I have a plus operator that returns const, and a prefix increment operator, this code is invalid:
class Integer{
int i;
public:
Integer(int ii): i(ii){ }
Integer(Integer&);
const Integer operator+();
Integer operator++();
};
int main(){
Integer a(0);
Integer b(1);
Integer c( ++(a + b));
}
To allow this sort of assignment, wouldn't it make sense to have the + operator return a non-const value? This could be done by adding const_casts, but that gets pretty bulky, doesn't it?
Thanks!
When you say ++x, you're saying "add 1 to x, store the result back into x, and tell me what it was". This is the preincrement operator. But, in ++(a+b), how are you supposed to "store the result back into a+b"?
Certainly you could store the result back into the temporary which is presently holding the result of a+b, which would vanish soon enough. But if you didn't really care where the result was stored, why did you increment it instead of just adding one?
FYI, ++(a + b) is illegal even with PODs (plain old data types, like int). So it makes sense not to allow it for your own class types either. Try this:
int a = 1;
int b = 2;
int c = ++(a+b);
GCC returns error: lvalue required as increment operand.
In your case, it would be preferable make your copy constructor take a const Integer argument, and create your Integer c like this instead:
Integer c(a + b + Integer(1));
Copy constructors usually take a const reference, solving that problem for you.
(Having non-const copy ctor implies some transfer of resources, which can be useful sometimes, but for 99% of all situations, it's not needed)
I believe OP's example would be suitable to the question if the addition operator is substituted with any other binary operator that returns a reference, for example an assignment operator:
Integer c( ++(a = b));
I came here wondering whether I should make my assignment operator return a const or a non-const reference. Some tutorials use non-const versions, contrary to "Thinking in C++"'s advice. And some other references give reasoning behind that:
Notice that the returned reference is not declared const. This can be a bit confusing, because it allows you to write crazy stuff like this:
MyClass a, b, c;
...
(a = b) = c; // What??
At first glance, you might want to prevent situations like this, by having operator= return a const reference. However, statements like this will work with primitive types. And, even worse, some tools actually rely on this behavior. Therefore, it is important to return a non-const reference from your operator=. The rule of thumb is, "If it's good enough for ints, it's good enough for user-defined data-types."