I am translating a java program to C++, and the architecture requires me to return a null pointer at certain points.
I have a pointer constructed as such:
auto p= std::make_unique< std::array< A, 3>>();
where A is of the form:
class A
{
public:
double x = 0, y = 0;
A(const double x, const double y):
x(x), y(y)
{}
};
Now, I would need to set the members through the pointer, so I would think:
p[0].x += 1.0;
Since unique_ptr has the dereferencing [] operator, but that fails with:
error: no match for 'operator[]' (operand types are 'std::unique_ptr<std::array<A, 3ull> >' and 'int')
I reviewed similar questions, but it is unclear to me if what I want to do is possible. Is the [] operator for c-style declared arrays only?
Is the [] operator for c-style declared arrays only?
Yes, it's only supported for the array version, i.e. unique_ptr<T[]>, std::array is not counted.
You can use operator* instead, like
(*p)[0].x += 1.0;
Related
I'm reading this tutorial on computer graphics, and ran across the following code example.
template<typename T>
class Vec3
{
public:
// 3 most basic ways of initializing a vector
Vec3() : x(T(0)), y(T(0)), z(T(0)) {}
Vec3(const T &xx) : x(xx), y(xx), z(xx) {}
Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {}
T x, y, z;
};
typedef Vec3<float> Vec3f;
Vec3<float> a;
Vec3f b;
I recognize that each constructor is making use of member initializer lists.
The default constructor initializes x, y, and z as 0 of type T
?
The constructor accepts x, y, and z explicility and initializes the member variables accordingly.
I'm not sure what 2. is trying to illustrate, or how I would use it. What does const T &xx mean exactly? I get that xx is a pointer to some variable of type T, but is the const referring to the pointer itself or the variable it's pointing to? Furthermore, what's the point of initializing x, y, and z with the same variable?
const T &xx is a reference (&) to a const T.
By using a reference instead of simply passing T xx, a potentially expensive copy can be avoided, in case T is a complex type. (Might be premature optimization in this case, because your typical Vec3 will only be instantiated with types like float or double, but the compiler will optimize it out anyway.)
By making it a reference to a const T, the caller can be certain that the T they pass in will not be modified by the constructor. Moreover, the caller will be able to pass values that are already const, like literals (e.g. Vec3<double>(1.0)).
The rule with const is: it always refers to the thing that precedes it. So int const *x means that x is a (mutable) pointer to a const int, whereas int *const x means that x is a const pointer to a (mutable) int. int const *const x is also possible.
But what if the const is the first token in the type, so there's nothing that precedes it? Then it "jumps over one", so const T &xx is the same as T const &xx. For consistency, I prefer to write the latter, but many people use the former because it reads more naturally from left to right.
This constructor would be useful if you want a vector set to (1, 1, 1), for instance. Of questionable mathematical meaning, but sometimes useful. For example, by multiplying a vector (x, y, z) by (1, 1, 1) we can easily compute the sum of the components of the former vector.
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 recently learning how to use the std::mem_fn library facility. In C++ Primer 5th edition they give an example of a usage of std::mem_fn, in the following code snippet svec is a vector of strings:
auto f = mem_fn(&string::empty); // f takes a string or a string*
f(*svec.begin()); // ok: passes a string object; f uses .* to call empty
f(&svec[0]); // ok: passes a pointer to string; f uses .-> to call empty
Note: In the above code snippet there is no use of ->*
Although I understand the usage .* and ->*. I don't have clarity on it.
So, My question is what does .*, ->*, .-> do?
You have an object o and a pointer to its member m. The operator .* allows you to access the pointed member of your object:
o.*m
You have a pointer to an object p and a pointer to its member m. The operator ->* allows you to access the pointed member of the pointed object:
p->*m
I am afraid that the operator .-> doesn't exist.
Operators .* and ->* call a member function (BTW there is no meaning for .-> in C++ - it's a syntax error).
These are binary operators. The left operand is an object (for .*) or a pointer to an object (for ->*), and the right operand is a pointer to a member (which can be a member field or a member function). The outcome of applying this operator is the ability to use a member field or a method.
Maybe these operators make sense from a historical point of view. Suppose you use a regular C pointer to a function:
typedef int (*CalcResult)(int x, int y, int z);
CalcResult my_calc_func = &CalcResult42;
...
int result = my_calc_func(11, 22, 33);
What if you decided to "rewrite" your program in an object-oriented way? Your calculation function would be a method in a class. To make a pointer to it, you need a new type, a pointer to member function:
typedef int (MyClass::*CalcResult)(int x, int y, int z);
CalcResult my_calc_func = &MyClass::CalcResult42;
...
MyClass my_object;
int result = (my_object.*my_calc_func)(11, 22, 33);
I always use this analogy when I try to remember the syntax of this .* operator - this helps because the regular pointer-to-function syntax is less convoluted and used more frequently.
Please note the parentheses:
int result = (my_object.*my_calc_func)(11, 22, 33);
They are almost always needed when using this .* operator, because its precedence is low. Even though my_object .* (my_calc_func(11, 22, 33)) is meaningless, the compiler would try to make sense of it if there were no parentheses.
The ->* operator works in essentially the same way. It would be used if the object on which to apply the method is given by a pointer:
typedef int (*MyClass::CalcResult)(int x, int y, int z);
CalcResult my_calc_func = &MyClass::CalcResult42;
...
MyClass my_object1, my_object2;
MyClass* my_object = flag ? &my_object1 : &my_object2;
int result = (my_object->*my_calc_func)(11, 22, 33);
The .* and ->* operators are called pointer to member access operators and like their name suggests they allow you to access an object's data or function member given an appropriate pointer to that class member.
struct foo
{
int a;
float b;
void bar() { std::cout << "bar\n"; }
};
int main()
{
int foo::* ptra = &foo::a;
float foo::* ptrb = &foo::b;
void (foo::* ptrbar)() = &foo::bar;
foo f;
foo * fptr = &f;
f.*ptra = 5;
f.*ptrb = 10.0f;
(f.*ptrbar)();
fptr->*ptra = 6;
fptr->*ptrb = 11.0f;
(fptr->*ptrbar)();
return 0;
}
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.
template <typename T>
class v3 {
private:
T _a[3];
public:
T & operator [] (unsigned int i) { return _a[i]; }
const T & operator [] (unsigned int i) const { return _a[i]; }
operator T * () { return _a; }
operator const T * () const { return _a; }
v3() {
_a[0] = 0; // works
_a[1] = 0;
_a[2] = 0;
}
v3(const v3<T> & v) {
_a[0] = v[0]; // Error 1 error C2666: 'v3<T>::operator []' : 2 overloads have similar conversions
_a[1] = v[1]; // Error 2 error C2666: 'v3<T>::operator []' : 2 overloads have similar conversions
_a[2] = v[2]; // Error 3 error C2666: 'v3<T>::operator []' : 2 overloads have similar conversions
}
};
int main(int argc, char ** argv)
{
v3<float> v1;
v3<float> v2(v1);
return 0;
}
If you read the rest of the error message (in the output window), it becomes a bit clearer:
1> could be 'const float &v3<T>::operator [](unsigned int) const'
1> with
1> [
1> T=float
1> ]
1> or 'built-in C++ operator[(const float *, int)'
1> while trying to match the argument list '(const v3<T>, int)'
1> with
1> [
1> T=float
1> ]
The compiler can't decide whether to use your overloaded operator[] or the built-in operator[] on the const T* that it can obtain by the following conversion function:
operator const T * () const { return _a; }
Both of the following are potentially valid interpretations of the offending lines:
v.operator float*()[0]
v.operator[](0)
You can remove the ambiguity by explicitly casting the integer indices to be unsigned so that no conversion is needed:
_a[0] = v[static_cast<unsigned int>(0)];
or by changing your overloaded operator[]s to take an int instead of an unsigned int, or by removing the operator T*() const (and probably the non-const version too, for completeness).
In simple terms: the compiler doesn't know whether to convert v to const float* and then use the operator[] for a pointer, or to convert 0 to unsigned int and then use the operator[] for const v3.
Fix is probably to remove the operator[]. I can't think of anything it gives you that the conversion operator to T* doesn't already. If you were planning to put some bounds-checking in operator[], then I'd say replace the conversion operators with getPointer functions (since in general you don't want to implicitly convert a safe thing to an unsafe thing), or do what std::vector does, which is that users get a pointer with &v[0].
Another change that lets it compile, is to change operator[] to take an int parameter instead of unsigned int. Then in your code, the compiler unambiguously chooses the interpretation with no conversion. According to my compiler, there is still no ambiguity even when using an unsigned index. Which is nice.
When you the compiler compiles the following
v[0]
it has to consider two possible interpretations
v.operator T*()[0] // built-in []
v.operator[](0) // overloaded []
Neither candidate is better than the other because each one requires a conversion. The first variant requires a user-defined conversion from v3<T> to T*. The second variant requires a standard conversion from int (0 is int) to unsigned int, since your overloaded [] requires an unsigned int argument. This makes these candidates uncomparable (neither is clearly better by C++ rules) and thus makes the call ambuguous.
If you invoke the operator as
v[0U]
the ambiguity will disappear (since 0U is already an unsigned int) and your overloaded [] will be selected. Alternatively, you can declare your overloaded [] with int argument. Or you can remove the conversion operator entirely. Or do something else to remove the ambiguity - you decide.
It is your type conversion operator that is the culprit. v transformed to a float pointer. Now there are two operator []s possible, one is the built in subscript operator for float and the other being the one you defined on v, which one should the language pick, so it is an ambiguity according to ISO.
Remember a class is a friend of itself:
v3(const v3<T> & v)
{
_a[0] = v._a[0];
_a[1] = v._a[1];
_a[2] = v._a[2];
}
When copy something of the same type you are already exposed to the implementation details. Thus it is not a problem to access the implementation directly if that is appropriate. So from the constructor you can access the object you are copying directly and see its member '_a'.
If you want to know the original problem:
The literal '1' in the context 'v[1]' is an integer (this is a synonym of signed integer). Thus to use the operator[] the compiler technically is required to insert a conversion from int to unisgned. The other alternative is to use the operator*() to get a pointer to the internal object and then use the [] operator on the pointer. Compiler is not allowed to make this choice and error out:
Compiler options:
_a[1] = v[1];
// Options 1:
_a[1] = v.operator[]((unsigned int)1);
// Options 2:
_a[1] = v.operator*()[1];
To make it unabigious you can use an unsigned literal;
_a[1] = v[1u];
In the long run it may be worth making this easier for the user.
Convert the operator[] to use int rather than unsigned int then you will get exact matches when integer literals (or you can have two sets of operator[]. One that uses int and one that uses unsigned int).
I didn't see it untill James McNellis posted the full error message, but the ambiguity is not between the two v3::operator[]() functions as it appears to be.
Instead, since there is no exact match between argument types, the compiler can't decide whether to:
a) Use v3::operator[](unsigned int) const, thereby converting the int argument to unsigned, or
b) Use the v3::operator const T*() const conversion followed by the built-in array indexing operator.
You can avoid this by making the operator[] arguments int's rather than unsigned ints. But a better solution would be to avoid an implicit conversion to T* and instead provide a Data() function that did that explicitly.
I had this same issue: I resolved it simply making the typecast operator explicit.
The const version doesn't modify anything. The non-const version allows you to assign things using array notation (v[3] = 0.5;).