For the following definition of
const vector3F operator*(const vector3F &v, float s);
There are two const, what are their respective usages?
The const-reference in the argument means that you don't change v, so you can pass constant vectors (and temporaries!) to the function. That's a Good Thing.
The constant by-value return is sort of a gimmick. It prevents you from writing things like this:
vector3F v = get_vector();
vector3F w = v;
(v * 1.5) = w; // outch! Cannot assign to constant, though, so we're good.
Returning by-value as constant is problematic, though, since it interferes with C++11's rvalue references and move semantics:
move_me(v * 1.5); // cannot bind to `vector3F &&` :-(
Because of that, and because an abuse like the one I showed above is fairly unlikely to happen by accident, it's probably best to return by value only as non-constant.
The first const indicates that the return value is constant and can not be altered (which, by the way, is a bad idea for the multiplication operator):
const Vector3F v = myvector*100.0;
v.x = 0; // error: the vector is constant and can not be altered
The second const indicates that the argument "v" is constant:
const vector3F operator*(const vector3F &v, float s)
{
v.x = 0; // error: "v" is constant
}
Related
I have a struct
struct Stuff {
float something (int& prereq) {
float s = prereq+2;
return s;
}
double something_else(int& prereq_ref, float& thing_ref, float& s_ref ){
s2 = s + thing + h;
return s2;
}
};
Then I run a call in my main loop
float thing = 4;
int prereq = 2;
int main() {
Stuff item;
double n = item.something_else(prereq, thing, item.something(prereq));
return 0;
}
The call in main doesn't run, however the following line does
float s = item.something(prereq);
double n = item.something_else(prereq, thing, s);
Am I missing something obvious? I'd rather not waste memory on what seems to be an unnecessary float.
float& is an lvalue reference type. It can only take values that can be assigned to, such as variables.
float s = item.something(prereq);
double n = item.something_else(prereq, thing, s);
Here, s is a variable. It has a place in memory and the expression s = ... would be meaningful. On the other hand,
double n = item.something_else(prereq, thing, item.something(prereq));
Here, the value is item.something(prereq), which is not an lvalue. We can't write item.something(prereq) = ...; it doesn't make sense to assign to the return value of that function.
If you're not planning to modify the function arguments, take them by constant reference or by value.
double something_else(const int& prereq_ref, const float& thing_ref, const float& s_ref)
or
double something_else(int prereq_ref, float thing_ref, float s_ref)
for large data like structures or classes, you might consider using const&, but for integers and floats, it's unnecessary overhead and by-value parameters will do fine.
int foo(int & arg);
This function signature says:
"I will take a reference to the variable you pass in, and I may change it while computing my return value."
If that statement is not true, then your function signature is wrong, or at least misleading to anyone looking at it. It's also worth noting, that if you compute a temporary, it's not in a variable and so it's ineligible to pass into this function. That is the problem you're running into.
Note, this style of function signature is what's known as an "out" parameter (non-const lvalue reference) because it can be thought of as returning an OUTput through a parameter. This design approach is discouraged if other approaches are available, and so it is actually somewhat rare. Any time you find a function with a non-const reference parameter, be sure it's what you mean.
Compare it to this:
int foo(int arg);
This function says:
"I get my own copy and don't care what it came from, variable, reference, temporary, whatever, and I will leave your origianl value alone when computing my return value."
This clearly is what you want to say, so drop the & in your parameter list.
I have a function that takes some arguments of type T as such:
constexpr inline const bool isReflex(const T x1, const T y1, const T x2, const T y2, const T x, const T y)
Calling this function with items form a vector yiels to an error C2664: Cannot convert argument 1 from 'vector<T, std::allocator<_Ty>>' to 'const T':
vector<T>* v = new vector<T>; // I am not creating the vector myself, this is just for demonstration.
// The real vector is passed as const vector<T>* to a function executing the following:
if (isReflex(v[i-2], v[i-1], v[i], v[i+1], v[i+2], v[i+3]))
// ^^^^^^ error
This makes little sense to me, as I am not passing the vector but rather its contents. What could be causing this behaviour?
Edit
Ouch.
This is because v is not a vector, it is a pointer to vector. Therefore, you need a dereference operator:
if (isReflex((*v)[i-2], (*v)[i-1], (*v)[i], (*v)[i+1], (*v)[i+2], (*v)[i+3]))
The reason the error message may not look entirely clear is that [] operator applies to pointers as well, and behaves like a dereference operator with an offset. In other words, C++ compiler treats variable v as a built-in array of vectors, applies index [i-2] to that array, and reports an error, because the type of v[i-2] expression is a vector.
The real vector is passed as const vector<T>* to a function
You can make a reference variable to keep the old syntax:
const vector<T> *pv // function parameter
const vector<T>& v = *pv;
// This will work now
if (isReflex(v[i-2], v[i-1], v[i], v[i+1], v[i+2], v[i+3])) {
...
}
You're using the [n] operator on an type * object - in your case vector<T> *. Your compiler might be interpreting this as "give me the nth vector starting to count from this address" instead of "give me the nth element from the vector pointed to by this address".
I have been looking through source code trying to learn more about C++ and I came across some code that looked confusing. I haven't been able to figure out its use by playing around with it.
Please can someone explain what the operator float *() does and how it is used?
class Vector
{
public:
float x,y,z;
Vector() : x(0), y(0), z(0){
}
Vector( float x, float y, float z ) : x(x), y(y), z(z){
}
operator float*(){
return &x;
}
operator const float *(){
return &x;
}
I have searched StackOverflow and it looks like it is a conversion operator but I am still unsure what it actually does and why it is useful.
Kind regards,
operator type_name declares an implicit conversion operator. In other words, this function is called when you are attempting to (implicitly) convert an object of your type to float* – for instance in an assignment:
Vector x(1, 2, 3);
float* f = x;
assert(*f == 1);
Needless to say, this particular conversion is quite terrible because its effect is pretty unintuitive and easily results in impossible to find bugs. Implicit conversions should generally be handled with care since they hide potentially confusing semantics. But they can be used well with types which are supposed to be used interchangeably, and where a conversion doesn’t hurt.
For instance, consider the case where you write your own integer and complex classes. A conversion from integer to complex is harmless, since every integer is a complex number (but not vice-versa). So having an implicit conversion integer → complex is safe.
As it was said it is a conversion operator that converts an object of type Vector to an object of type float *. This conversion operator can be called implicitly because it has no function specifier explicit.
I think that the idea of introducing this operator was to access data members x, y, z as an array of floats. In this case you could apply some standard algorithms to an object of the class converting it to an array of floats.
Take into account that the second overloaded operator-function shall have qualifier const.
Consider the following code
#include <iostream>
#include <algorithm>
class Vector
{
public:
float x,y,z;
Vector() : x(0), y(0), z(0){
}
Vector( float x, float y, float z ) : x(x), y(y), z(z){
}
operator float*(){
return &x;
}
operator const float *() const {
return &x;
}
};
int main()
{
Vector v( 1, 3, 2 );
auto max = std::max_element( v + 0, v + 3 );
std::cout << *max << std::endl;
return 0;
}
The output is 3.
Take into account that according to the C++ Standard
13 Nonstatic data members of a (non-union) class with the same access
control (Clause 11) are allocated so that later members have higher
addresses within a class object
So the order of the data members of the class is defined.
It's a conversion operator allowing the class to be used with APIs that want a float*.You have two different versions because one will convert the class to a const float* and the other to a non-const. The non-const conversion operator breaks your class's encapsulation though.
Usefulness: Well it can be very handy when, as I mentioned, you want to work with a library that expects a certain type. It can also be useful when there is a natural conversion between two types.
I have a topic I'm confused on that I need some elaborating on. It's operator overloading with a const version and a non-const version.
// non-const
double &operator[](int idx) {
if (idx < length && idx >= 0) {
return data[idx];
}
throw BoundsError();
}
I understand that this lambda function, takes an index and checks its validity and then returns the index of the array data in the class. There's also a function with the same body but with the function call as
const double &operator[](int idx) const
Why do we need two versions?
For example, on the sample code below, which version is used in each instance below?
Array a(3);
a[0] = 2.0;
a[1] = 3.3;
a[2] = a[0] + a[1];
My hypothesis that the const version is only called on a[2] because we don't want to risk modifying a[0] or a[1].
Thanks for any help.
When both versions are available, the logic is pretty straightforward: const version is called for const objects, non-const version is called for non-const objects. That's all.
In your code sample a is a non-const object, meaning that the non-const version is called in all cases. The const version is never called in your sample.
The point of having two versions is to implement "read/write" access for non-const objects and only "read" access for const objects. For const objects const version of operator [] is called, which returns a const double & reference. You can read data through that const reference, but your can't write through it.
To supply a code example to complement the answer above:
Array a(3);
a[0] = 2.0; //non-const version called on non-const 'a' object
const Array b(3);
double var = b[1]; //const version called on const 'b' object
const Array c(3);
c[0] = 2.0; //compile error, cannot modify const object
I think if there was an option like
double k = 3.0;
and the array's elements were const
a[0] = a[1] + k; or std::cout << a[0] + k;
the "const double &operator[](int idx) const" version would have been called, here you are adding non const variable to const object;
I've already overloaded operator [ ] to enable element access.
definition
double Matrix::operator[ ](const & int i){
return data[i]; // data[] is a member array: thrust:: device_vector
}
usage
Matrix m = ... // initialize
std::cout << m[3] << std::endl;
But now I want to access element by index so that assign new value to it.
usage
m[3] = 0.;
I understand the return value of the operator overload function must be an lvalue. I guess I should return a reference, but not sure how this is done. Thanks!
EDIT
Now I changed my definition to double &, but it still complains:
error: initial value of reference to non-const must be an lvalue
The array refers to a thrust::device_vector, which can be assigned value by index:
Thrust::device_vector<double> M(10);
M[3] = 0.;
Just replace
double ....
with
double& .....
change your definition to
double& Matrix::operator[ ](const & int i){
return data[i]; // data[] is a member array
}
Note, you should not use reference for an integer argument. It is usually used for complex classes for avoiding the copy. I would also suggest as a side note, to use m_memberVariable unless you use pimpl. This is what you should write:
double& Matrix::operator[ ] (const int i) {
return m_data[i]; // data[] is a member array
}
Note, you will need to add the '&' marker also in the class header where the method is declared.
This is all provided you have something like this in the class header:
class Matrix
{
private:
double m_data[100];
public:
double& operator[] (const int i);
};
Here you can read further details on the subscript operator overloading. This is another url for learning more about references.
As for a bit of inline explanation why it works like that:
You are trying to use the subscript operator on the left hand for assignment, hence the return value of such an operator overload has to be an l-value as you correctly state it. This is because if you return a value instead of reference, the left hand side in the usage would simply return that value which would evaluate to x = 0 where replace x with a value like 1. That is why you will get a compiler error in such cases.
Usually you chose to have a pair of operators. One for access:
double Matrix::operator[ ] const (const & int i){
return data[i];
}
and the other for assigment:
double &Matrix::operator[ ](const & int i){
return data[i];
}
If you call [ ] operator on a const object the first operator is really called. In other cases, both in access as well as assgnement, the second one is called.
You get error:
error: initial value of reference to non-const must be an lvalue
because data is (probably) a local variable. Make it a Matrix class private data member if it's not already.