C++: Overloading the [ ] operator for read and write access - c++

In general, how do you declare the index [ ] operator of a class for both read and write accesss?
I tried something like
/**
* Read index operator.
*/
T& operator[](T u);
/**
* Write index operator
*/
const T& operator[](T u);
which gives me the error
../src/Class.h:44:14: error: 'const T& Class::operator[](T)' cannot be overloaded
../src/Class.h:39:8: error: with 'T& Class::operator[](T)'

Your mutable version is fine:
T& operator[](T u);
but the const version should be a const member function as well as returning a const reference:
const T& operator[](T u) const;
^^^^^
This not only distinguishes it from the other overload, but also allows (read-only) access to const instances of your class. In general, overloaded member functions can be distinguished by their parameter types and const/volatile qualifications, but not by their return types.

You simply have one overload that'll be used for both reads and writes:
T& operator[](int);
Having said that, you might also want to have a const overload:
const T& operator[](int) const;
This will provide read-only indexing into const instances of your class.

You get the error, because overloaded functions cannot differ only by return type. But they can differ by const-ness.
/**
* Write index operator.
*/
T& operator[](T u);
/**
* Read index operator
*/
const T& operator[](T u) const;
Note "write" and "read" swapped places.
Also, don't you actually mean the argument of the operator to be some integral type ?

Related

user defined data type in set in c++

In the first case, code is working fine but I am getting an error in the second code, the only difference is of "const" in the operator overloading of '<', I am not able to figure out why.
Code for both cases are below
1.
class first
{
public:
int y;
bool operator < (first t) const
{
return (y>t.y);
}
};
set<first> f;
2.
class first
{
public:
int y;
bool operator < (first t)
{
return (y>t.y);
}
};
set<first> f;
The default comparator for std::set is std::less<Key> and as we can see on std::less it defines:
constexpr bool operator()( const T& lhs, const T& rhs ) const;
Which accepts const arguments and returns lhs < rhs. This of course doesn't work if lhs < rhs is not valid for const arguments, as it is in your second case because bool operator < (first t) cannot be called.
Writing your own comparator which accepts non-const arguments doesn't seem to work either, so it looks it is a requirement of the ordered container, but further check with the standard is needed to confirm.
std::set, like most other containers in the standard library, uses the requirement Compare for its comparing function. And the Compare requirement enforces constness:
As with any BinaryPredicate, evaluation of that expression is not allowed to call non-const functions through the dereferenced iterators.

Why does implicit conversion to constant iterator fail in this code?

This relates to my previous question here.
Here's a summary of that thread: I am trying to implement a doubly-linked-list class called my_list in C++, including iterators and I want automatic implicit conversion of iterator to const_iterator.
My original thought was to have something like
template<class T>
class my_list_iterator<T>
{
node<T> pos_;
/* code */
operator my_list_iterator<const T>(){return my_list_iterator<const T>(pos_);}
};
and then inside my_list define iterator as my_list_iterator<T> and const_iterator as my_list_iterator<const T>.
However, as pointed out by Miled Budneck in the previous thread, this won't work because a pointer to node<T> cannot convert to a pointer to node<const T>. He suggested I re-implement the const iterator as a pointer to const node instead.
While that works, by looking around I also found one other possible way to define the iterator class:
template<class T, class Pointer, class Reference>
class my_list_iterator {
node<T>* pos_;
/* code */
operator my_list_iterator<T,const Pointer,const Reference>() {return my_list_iterator<T,const Pointer,const Reference>(pos_);}
};
Then I can define my_list<T>::iterator as my_list_iterator<T,T*,T&> and my_list<T>::const_iterator as my_list_iterator<T,const T*,const T&>. I thought the last line would take care of the implicit conversion issue, but it seems to not be used at all.
For reference, here is the complete code:
template<class T> class node {
node(const T& t = T()):data(t),next(0),prev(0) {}
T data;
node* next;
node* prev;
friend class my_list<T>;
template<class U,class Pointer,class Reference> friend class my_list_iterator;
};
template<class T,class Pointer,class Reference> class my_list_iterator {
public:
// increment and decrement operators
my_list_iterator operator++();
my_list_iterator operator++(int);
my_list_iterator operator--();
my_list_iterator operator--(int);
// bool comparison iterators
bool operator==(const my_list_iterator& other) const {return pos_==other.pos_;}
bool operator!=(const my_list_iterator& other) const {return pos_!=other.pos_;}
// member access
Reference operator*() const {return pos_->data;}
Pointer operator->() const {return &(pos_->data);}
// conversion to constant
operator my_list_iterator<T,const Pointer,const Reference>() {return pos_;}
private:
node<T>* pos_;
explicit my_list_iterator(node<T>* p=0):pos_(p) {}
friend class my_list<T>;
};
and the error message in the end
note: no known conversion for argument 1 from ‘my_list<int>::iterator’ {aka ‘my_list_iterator<int, int*, int&>’} to ‘const my_list_iterator<int, const int*, const int&>&’
So why does this code not work? Is there any small modification I can make to make it work, or is this design impossible to implement?
Given Pointer = T*, const Pointer is T* const, not const T*. Type substitution is not like macro expansion. const Pointer adds a top-level const to the type denoted by Pointer. Similarly, const Reference is T& const (which is automatically adjusted to T& because top-level cv-qualifiers on references are ignored) instead of const T&. (See What is the difference between const int*, const int * const, and int const *? for the difference between T* const and const T*.)
You need to use something like const std::remove_pointer<Pointer>* for this to work.

C++ Insertion operator overload (<<)

I'm working through an assignment in which I must overload the insertion operator to take a Node object. I've created the operator overload function outside the class definition, but inside the node.h file. Everything compiles fine, but the overloaded operator is not called, instead I get simple the address of the object.
I'm prohibited from modifying the calling code, so any changes must be to the operator overload.
My code as it stands right now:
/** OPERATOR << ***********************************/
template<class T>
inline std::ostream & operator << (std::ostream & out, const Node <T> *& pHead)
{
out << "INCOMPLETE";
return out;
}
Right now, I just want to ensure the overloaded operator is called. I'll fix the output code once I know I'm calling the right operator.
The calling code:
// create
Node <char> * n = NULL;
// code modifying n
// display
cout << "\t{ " << n << " }\n";
Note that the parameter pHead's type is a reference to non-const, const Node<T>* is a non-const pointer to const, the argument n's type is Node<T>* (i.e. a non-const pointer to non-const). Their type doesn't match, Node<T>* need to be converted to const Node<T>*, which is a temporary and can't be bound to reference to non-const.
In short, you can't bind a reference to non-const to an object with different type.
But reference to const could be bound to temporary, so you can change the parameter type to reference to const:
template<class T>
inline std::ostream & operator << (std::ostream & out, const Node <T> * const & pHead)
// ~~~~~
Or change it to passed-by-value, Node<T>* will be implicitly converted to const Node<T>* when passing as argument. (Passing pointer by reference to const doesn't make much sense.)
template<class T>
inline std::ostream & operator << (std::ostream & out, const Node <T> * pHead)
At last, overloading operator<< with pointer type looks weird. The most common form with user-defined type would be:
template<class T>
std::ostream & operator << (std::ostream & out, const Node <T> & pHead)
The problem is that the inserter takes a parameter of type const Node<T>*, but it's called with an argument of type Node<T>*; there is no conversion from T* to const T*. So the "fix" is to remove the const from the stream inserter.
But, as hinted at in a comment, having an inserter that takes a pointer to a type is a bad idea. It should take a const Node<T>&, like all the other inserters in the world. I gather that this is a constraint imposed by an assignment; if so, it's idiotic. You're being taught badly.

User-defined class in std::map: Invalid operands to binary expression

I want to use a class as the key in a std::map.
std::map<Type, value> collection;
Although I defined operator<, the key isn't accepted: Invalid operands to binary expression ('const Type' and 'const Type').
class Type {
public:
inline bool operator< (const Type& rhs) { /* ... */ }
Why is this happening?
You must define your operator< as
inline bool operator< (const Type& rhs) const { /* ... */ }
because map stores const key internally.
To expand a bit I'd also suggest (like dyp already did in the comments) That you use non-member operator overloads when possible. There are various advantages to them. I won't list them here since there already is good information on the advantages/differences, just let me link you to Operator overloading : member function vs. non-member function?.

How to implement both scalar and vector addition using the += operator?

I'm working on a Vector2D class, and I think both vector addition and scalar addition make sense to be implemented with the +=/+ operators.
Trouble is, I don't really know how to work around this apparent argument ambiguity, here's what Clang says:
vector2d_test.cpp:17:16: error: use of overloaded operator
'+=' is ambiguous (with operand types 'Vector2D<float>' and 'int')
vector += 1;
~~~~~~ ^ ~~~~~~~
vector2d.hpp:34:18: note: candidate function
Vector2D<T>& operator+=(const Vector2D<T>& other)
^
vector2d.hpp:41:18: note: candidate function
Vector2D<T>& operator+=(const T summand) const
Here are the two functions:
Vector2D<T>& operator+=(const Vector2D<T>& other)
{
x += other.x;
y += other.y;
return *this;
}
template <typename S>
Vector2D<T>& operator+=(const S summand) const
{
x += summand;
y += summand;
return *this;
}
So... any idea what I can do about this?
You have all written in you error message. You tried to add to your vector variable of int type, but your vector has floats. It should be:
vector += 1f;
or
vector += 1.0;
Take a look. When you have this vector:
Vector2D<float> vector;
function corresponding to this vector has header:
Vector2D<T>& operator+=(const float summand) const;
Second one doesn't matter right now. And when you try to add to your vector 1, you are trying to invoke function:
Vector2D<T>& operator+=(const int summand) const;
Which you didn't declare. That's why compiler message you an error - it can't find proper function.
It's not clear what you're trying to do. The operator+=
functions you post aren't legal unless they are members. And if
they are members, and you have something like:
Vector2D<float> v;
// ...
v += 1;
the Vector2D<float>::operator+=( Vector2D<float> const& )
function isn't callable, and so there can be no ambiguity. If
the functions aren't members, then they should be written:
template <typename T>
Vector2D<T>& operator+=( Vector2D<T>& lhs, Vector2D<T> const& rhs );
template <typename T, typename U>
Vector2D<T>& operator+=( Vector2D<T>& lhs, U rhs );
Even in this case, the first cannot be called with an rhs of
type int, so there is no ambiguity.
EDIT:
I missed the const at the end of the second in your posting.
This is an obvious typo on your part, It still doesn't change
anything, unless you also have some implicit conversions to
Vector2D (which is probably not a good idea); otherwise, the
first version is still not callable. If there is, for example,
an implicit conversion from int to Vector2D, and you call
+= on a non-const Vector2D, then the first overload is
a better match for the implicit first argument (which results in
the this pointer), since it is an exact match, without even
a cv conversion, but the second function is a better match for
the second argument, because the template instantiation results
in an exact match. So the call is ambiguous.
Easiest way is to define the functions inside Vector2D ala:
Vector2D& operator+=(const Vector2D& rhs)
{ ...each this element += rhs's corresponding element... }
Vector2D& operator+=(const T& summand)
{ ...each this elements += summand... }
friend Vector2D operator+(Vector2D lhs, const Vector2D& rhs) { return lhs += rhs; }
friend Vector2D operator+(Vector2D lhs, const T& rhs) { return lhs += rhs; }
Notes:
the non-member functions are friends, which allows them to be defined conveniently inline
the functions shouldn't be const (one was in your question)
usual conversions will be attempted, so if T is float then ints will work just fine unless you do something funny like give Vector2D an implicit constructor from a single int that makes the choice of conversion ambiguous