Why does it require to be a member function of a class for its operation and is good to return a reference to private member?
class X
{
public:
int& operator[] (const size_t);
const int &operator[] (const size_t) const;
private:
static std::vector<int> data;
};
int v[] = {0, 1, 2, 3, 4, 5};
std::vector<int> X::data(v, v+6);
int& X::operator[] (const size_t index)
{
return data[index];
}
const int& X::operator[] (const size_t index) const
{
return data[index];
}
As to why is it required to have [] as a member, you can read this question (by yours sincerely). Seems it's just the way it is with no really really convincing explanation.
As to why return reference?
Because you want to provide a way not only to read, but also (for non-const objects) to modify the data. If the return weren't a reference (or some proxyr)
v[i] = 4;
wouldn't work.
HTH
It needs to be a member function according to 13.5.5:
operator[] shall be a non-static
member function with exactly one
parameter. It implements the
subscripting syntax
A reference to a private member is completely OK and pretty common. You hide the details from the user of your class, but still provide the functionality you need (ability to modify individual elements)
Your data variable likely shoudn't be static though, unless you really want to share it among all instances of your class
For the first question, it is just the way they decided it had to be, i.e. you can't do:
T operator[]( const X &, size_t );
as an external function.
And yes, you are fine returning a reference to a private member, non-const if you allow your users to write there, non-const otherwise.
In your example though data is static, which does not make sense if that is the source for what it returns.
What would the syntax be for calling a non-member operator[]? Any syntax for that would be awkward. operator[] takes ones parameter within the [ and ] and that is usually an index or some kind of data necessary to find an object.
Also, yes, it is a good idea to return a reference, even if it's a private member. That is exactly what STL vectors do and just about any other class I can think of that I've ever used that provides operator[]. It would be advised that it the usage is maintained.
Related
I have a class like
struct S {
bool foo(const AType& v) const {
return values.count(&v); // compile error due to the constness of v
}
private:
std::set<AType*> values;
};
This is a simplified version. In real code, foo does some complex things.
The code produces an error
invalid conversion from ‘const AType*’ to ‘std::set<AType*>::key_type {aka AType*}’
I think foo should take 'const AType& v' because it does not mutate v.
The type of the member variable 'values' can not be std::set<const AType*> because some methods of the struct S call non-const methods of the elements contained in 'values'.
I can cast constness of 'v' away:
bool foo(const AType& v) const {
return values.count((AType*) &v);
}
But I think this may not be a good solution in general.
What solution could I have?
template<class T>
struct const_ptr_compare:std::less<T const*> {
typedef void is_transparent;
};
std::set<AType*, const_ptr_compare<AType>> values;
and bob is your uncle.
I defined a comparator that can transparently handle T const* comparisons as well as T* comparisons. I then told std::set that it is transparent.
The is_transparent technique is a C++14 enhancement to std::set. Your compiler may already have support for it.
If you want to be able to pass in base-classes-of-T a bit more work needs to be done. You need a function object that tests its two arguments (each pointers to const) to see which is a base class of the other, and does a std::less<base const*>{}(lhs, rhs) using that base class. However, that is going further down the rabbit hole than we need to.
A C++11/03 approach could involve making the editable portions of AType mutable, and having a const AType* set. Failing that, a const_cast<AType*> is reasonable.
Please don't use a C-style cast: they are almost never required, and they can be accidentally too powerful.
The root of the problem is this:
The type of the member variable 'values' can not be std::set because some methods of the struct S call non-const methods of
the elements contained in 'values'.
You are not supposed to mutate the set key in any way.
You should be using a map, where the actual key parts are in the key and are const, and the mutating parts are in the value and are non-const.
If this is impractical for your design, I understand. The const_cast workaround will solve the issue. It feels inelegant, but that's because your usage of set is inelegant.
This is just one of the many problems of the const correctness concept: it doesn't scale by composition.
The casting solution is just ok, if you feel it's "dirty" in some way probably you're overestimating cleanness of the const concept.
Consider that defining special types for const_iterator and const_reverse_iterator was needed or that sometimes const correctness requires actual code duplication (e.g. in many cases for operator[]) or that if you've an object containing a non-const pointer you can mutate the pointed-to object freely and even delete it in const members.
Your cast doesn't look that bad in comparison :-)
I am trying to interface a C library to my C++ project. The library has its own vector type, assume to be VECTOR, and it provides element access:
int vector_set_value(VECTOR* vec, int index, double new_value);
int vector_get_value(VECTOR* vec, int index, double* retrieved_value);
Now it would be good to wrap the get and set operations by operator[] overloading
double& operator[](int index);
const double& operator[](int index) const;
But how do I tell operator[] have different behavior, between vec[index]=3 and double value=vec[3]? For the prior vector_set_value should be called while for the latter vector_get_value should be called.
I would not attempt to wrap one interface into the other.
That being said, if you really want to do this one possible solution is to create a proxy object, and have your operator[] return that proxy object. The proxy object would have conversions into the underlying type const double& for reading, and overloaded operator= for writing. Each one of them would call the appropriate library function.
This will allow for syntax that looks like that of C++ std::vector: MyVector v(...); v[1] = 10.1; double d = v[1]; but it will be problematic. The proxy object cannot replace the real type in all contexts, only in some of them. And even there, the semantics are different, so while it may look like a regular vector, one day you will try to use the proxy in a way it does not support and you will be puzzled at the leaking abstraction
I think I don't really understand what's behind references, and I'd be glad to learn more about those.
I'm writing a math "vector" Class to do basic linear algeabra for numerical simulation. I was using Eigen before i was convinced not to use external libraries anymore. My problem is pretty straightforward :
I declare vector and sets its 3 components of type Scalar (these are doubles). I can do math with my vectors as I overload operators, but this is beyond the scope of my question.
I want to access the i-th component of my object through the function call operator () as I was used with Eigen : myVector(0) = 0.0 ; or Scalar d = myVector(0)+1.0 ;
According to my understanding of references, this solution should be working :
class mtnVector {
public:
typedef double Scalar;
Scalar data [3];
(setters, math, etc...)
inline Scalar & operator() (const int i) const {
return data[i] ;
}
};
But g++ says that it doesn't like the way I implement it and comfirms I s*** at references :
Vector.h:185: error: invalid initialization of reference of type ?double&? from expression of type ?const double?
What's very strange from my point of view is that if the array containing the data is dynamically set (Scalar * data) (with new operator) at class construction, the code compiles fine. But I don't see the point of having dynamically set data holder.
I don't get neither the need of const to overload the function call operator but I accept it.
Your operator() is declared const. This means that calling the function should never end up modifying the object. That function returns a member of this object by non-const reference, which would allow whoever called operator() to modify the internals of the object. Obviously this would be silly, so the compiler just doesn't allow it. If you're going to return a reference to a member from a const member function, you need to make that reference const:
inline const Scalar& operator() (const int i) const {
return data[i] ;
}
You might want to provide both a const and non-const version of the function, one of which returns a const Scalar& and the other a Scalar& (this is how the standard library containers do it).
It seems strange that you'd want to use operator() for this. Your expression myVector(0) would look more natural as myVector[0], which you can achieve through overloading operator[] instead.
Also, you should ignore whoever convinced you that you shouldn't use external libraries. Eigen, in particular, is a very mature and thoroughly tested library. Unless you really have a good reason, you should be using it.
I'm implementing a class Aviary, which can store pointers to Bird-objects. Now, I have the following:
class Aviary {
public:
const Bird &operator[](const size_t index) const {
return birds[index];
}
Bird &operator[](const size_t index) {
return birds[index];
}
private:
std::vector<Bird*> birds;
The Bird-objects are stored as pointers in order to avoid object-slicing. However, there is a problem with the operator[]-implementation (Reference to type 'const Bird' could not bind to an lvalue of 'const value_type' (aka 'Bird *const')).
How do I implement the operator[] properly?
Since you store pointers, you should dereference the pointer for return reference.
const Bird &operator[](const size_t index) const {
return *birds[index];
}
Bird &operator[](const size_t index) {
return *birds[index];
}
Side note: use smart pointers, instead of raw pointers.
Two side notes:
The const in a parameter passed by value (const size_t index) is useless and your compiler will ignore it. You can try declaring it with const and removing the const in the implementation: the compiler will correctly consider that your implementation matches the declaration.
The canonical way to implement the non-const version of operator[] is as follows:
As follows
Bird &operator[](size_t index) {
return const_cast<Bird&>(const_cast<const Aviary*>(this)->operator[](index));
}
I know all those const_cast look ugly, but they are both safe and this is the right way to ensure that both versions of operator[] do the same (you just need to maintain the const version from now on), while also making sure that your are not doing any non-const operation in the const version.
Apart from that, the problem with your code is that you are returning pointers, not (references to) the values pointed by them, as Luchian and ForEveR have already pointed out.
You need to dereference:
return *(birds[index]);
birds[index] is a Bird*, so you can't directly return it as a Bird&.
I am wondering if there is a way to overload operator[] for a non-class type in C++.
Basically, there is a data type which is a pointer (CFDictionaryRef from CoreFoundation). But it's not a class (i know that overloading operator[] for a specific class is allowed). I know how to access each element inside the CFDictionaryRef (for example, by using CFDictionaryGetIndex(CFIndex index); ). I want to make it simplified so that I don't have to write that function call every time. I want to overload the operator[ ] for CFDictionaryRef. But since it's not a class, from what I see, it's not possible.
Anyone got any suggestions?
You're right, it's not possible to overload operators on non-user-defined types.
What you might do is wrap the pointer in a class and overload the operators on the class itself. Since you can only overload operators on class types, this is the only option.
class CFDictionaryRefWrapper {
public:
CFDictionaryRefWrapper(CFDictionaryRef r) : dref(r) { }
CFDictionaryRef dref;
Type operator[](unsigned int index) {
/* do whatever with dref */
}
};
This also has the advantage of being able to automatically manage the lifetime of the pointer (RAII) if you need to do that.
No, you cannot overload the [] operator for a pointer type, or any other built-in type. In fact, ptr[N] already is a shorthand for *(ptr + N).
You'd need to define your own class which wraps the pointer if you want to overload the [] operator.