I want to write my own 2d array class. I want the class to be able to assign a value to an element like this.
a(0,0) = 1
I know this must be possible because I can do it with the matrix classes from blaze and eigen. My first idea was to overload the function operator (), which already works great to access the elements. When I try to assign a value with a(0,2) = 0; I get a compiler error.
lvalue required as left operand of assingment
What operator do I have to overload that the assignment also works?
You need to build a function with this prototype:
T& operator()(std::size_t, std::size_t);
Where T is your element type. This function needs to return a reference to an element in the array. This allows you to modify the value of the element via that reference in the caller.
As well as the above, you ought to provide the function
const T& operator()(std::size_t, std::size_t) const;
for read-only element access. I've rather dogmatically insisted on std::size_t for your indexing. In reality, you might want a typedef of your own to stand in for the index type.
You need to provide two overloads - one const and another one not, returning a reference. For example:
template <typename TVal> class val {
std::map<int, TVal> values;
public:
// the distinction is between a const version ...
TVal operator()(int key) const {
auto found = values.find(key);
if (found != values.end())
return found->second;
return TVal();
}
// ... and a non-const, returning a reference
TVal& operator()(int key) {
return values[key];
}
};
and then you can use it as follows:
val<int> v;
v(1) = 41;
v(1)++;
std::cout << v(1) << std::endl; // -> 42
std::cout << v(2) << std::endl; // -> 0
Please post the compile-error, for others to see and precisely answer it.
Having said that, I am pretty sure that it is because you are not returning a reference from your operator.
Related
Contrived example, for the sake of the question:
void MyClass::MyFunction( int x ) const
{
std::cout << m_map[x] << std::endl
}
This won't compile, since the [] operator is non-const.
This is unfortunate, since the [] syntax looks very clean. Instead, I have to do something like this:
void MyClass::MyFunction( int x ) const
{
MyMap iter = m_map.find(x);
std::cout << iter->second << std::endl
}
This has always bugged me. Why is the [] operator non-const?
For std::map and std::unordered_map, operator[] will insert the index value into the container if it didn't previously exist. It's a little unintuitive, but that's the way it is.
Since it must be allowed to fail and insert a default value, the operator can't be used on a const instance of the container.
http://en.cppreference.com/w/cpp/container/map/operator_at
Now that with C++11 you can have a cleaner version by using at()
void MyClass::MyFunction( int x ) const
{
std::cout << m_map.at(x) << std::endl;
}
Note for new readers.
The original question was about STL containers (not specifically about the std::map)
It should be noted there is a const version of operator [] on most containers.
It is just that std::map and std::set do not have a const version and this is a result of the underlying structure that implements them.
From std::vector
reference operator[](size_type n)
const_reference operator[](size_type n) const
Also for your second example you should check for a failure to find the element.
void MyClass::MyFunction( int x ) const
{
MyMap iter = m_map.find(x);
if (iter != m_map.end())
{
std::cout << iter->second << std::endl
}
}
Since operator[] might insert a new element into the container, it can't possibly be a const member function. Note that the definition of operator[] is extremely simple: m[k] is equivalent to (*((m.insert(value_type(k, data_type()))).first)).second. Strictly speaking, this member function is unnecessary: it exists only for convenience
An index operator should only be const for a read-only container (which doesn't really exist in STL per se).
Index operators aren't only used to look at values.
If you declare your std::map member variable to be mutable
mutable std::map<...> m_map;
you can use the non-const member functions of std::map within your const member functions.
It's possible to overload operator[] to take one argument, in the brackets. However how can I overload this operator to allow the user to type object[a] = b?
You return a reference to your item, something like:
A &operator[](int pos) { return list[pos]; }
So when you say object[a]=b; it's actually translated to:
A &tmp=object[a];
tmp=b;
Or in more clear terms:
A *tmp=object[a]; // more or less a pointer
*tmp=b; // you replace the value inside the pointer
The general declaration of the operator (it shall be a class member function) is the following
ObjectElementType & operator []( size_t );
Subscript operators often comes in pair :-
T const& operator[] (size_t ) const;
T& operator[] (size_t ); // This enables object[a] = b on non-const object
I am writing a multi-dimensional array class for which the number of dimensions is not known until runtime.
I have gotten to the subscript operator and would like to replicate the behavior of a multidimensional native array so that:
when using fewer subscripts than the number of dimensions, it will return an array (what might be called a "view")
when using a number of subscripts equal to the number of dimensions it will return the value stored at that location
Is it possible to have the return type depend on the relationship of the number of arguments passed to a function and a value that is not known until runtime (the number of dimensions)?
Other info:
I am implementing it as a sudo-multidimensional array by using a flat array and math to determine the correct index.
I am writing the subscript operator as a variadic function (non-templated).
The requirements,
when using ... it will return an array
when using ... it will return the value
contradict the standard (1.3.11) - a method or an operator cannot be overloaded on return types. So essentially it will be impossible for you to have something simple like:
class MyIndex; // defined elsewhere
template <typename T> struct MyArray {
const std::array<T>& operator[] (const MyIndex& x) {...}
const T& operator[] (const MyIndex& x) {...}
const T& operator[] (int, int, int) {...}
};
As noted in the comments you could return a boost::variant or your own object instead. The variant is the better idea of course. The advantage of your own object is that you'll be able to control the conversions directly, but I don't consider this as a particularly good idea:
class Result;
...
struct MyArrray {
const Result& operator[] (const MyIndex& x) {...}
const Result& operator[] (int, int, int) {...}
then add user defined conversions for Result or possibly separate getter operations
to examine and retrieve values:
class Result {
bool hasInt() const;
operator int() const { /* throw if wraps a view else return the value */ }
bool hasView() const;
operator MyArray() const { /* throw if wraps a value else return your array as a view */ }
};
You can already note that this quickly becomes a quagmire. It'll be hard to maintain, hard to reason about, and your code will not be explicit to the readers or reviewers.
Contrived example, for the sake of the question:
void MyClass::MyFunction( int x ) const
{
std::cout << m_map[x] << std::endl
}
This won't compile, since the [] operator is non-const.
This is unfortunate, since the [] syntax looks very clean. Instead, I have to do something like this:
void MyClass::MyFunction( int x ) const
{
MyMap iter = m_map.find(x);
std::cout << iter->second << std::endl
}
This has always bugged me. Why is the [] operator non-const?
For std::map and std::unordered_map, operator[] will insert the index value into the container if it didn't previously exist. It's a little unintuitive, but that's the way it is.
Since it must be allowed to fail and insert a default value, the operator can't be used on a const instance of the container.
http://en.cppreference.com/w/cpp/container/map/operator_at
Now that with C++11 you can have a cleaner version by using at()
void MyClass::MyFunction( int x ) const
{
std::cout << m_map.at(x) << std::endl;
}
Note for new readers.
The original question was about STL containers (not specifically about the std::map)
It should be noted there is a const version of operator [] on most containers.
It is just that std::map and std::set do not have a const version and this is a result of the underlying structure that implements them.
From std::vector
reference operator[](size_type n)
const_reference operator[](size_type n) const
Also for your second example you should check for a failure to find the element.
void MyClass::MyFunction( int x ) const
{
MyMap iter = m_map.find(x);
if (iter != m_map.end())
{
std::cout << iter->second << std::endl
}
}
Since operator[] might insert a new element into the container, it can't possibly be a const member function. Note that the definition of operator[] is extremely simple: m[k] is equivalent to (*((m.insert(value_type(k, data_type()))).first)).second. Strictly speaking, this member function is unnecessary: it exists only for convenience
An index operator should only be const for a read-only container (which doesn't really exist in STL per se).
Index operators aren't only used to look at values.
If you declare your std::map member variable to be mutable
mutable std::map<...> m_map;
you can use the non-const member functions of std::map within your const member functions.
for_each accepts InputIterators :
//from c++ standard
template <class InputIterator, class Function>
Function for_each (InputIterator first, InputIterator last, Function f);
is it ok to change the object in Function f, like this :
struct AddOne
{
void operator()(int & x){x = x + 1;}
};
std::vector<int> vec(10);
std::for_each(vec.begin(),vec.end(),AddOne());
This code works in VC++2008 and also with GCC, but is it also portable (legal) code ?
(InputIterators are only guaranteed to be usable as rvalue, in this case they are used as lvalue in AddOne's operator())
Read this article.
To be pedantic: for_each is a non-modifying sequence operation. The intent is not to modify the sequence. However, it is okay to modify the input sequence when using for_each.
You misunderstand something. Saying input iterators are only guaranteed to be usable as rvalues doesn't mean you can't get an lvalue out of an iterator somehow. So it does not mean that the result of *iterator is an rvalue. What you/for_each passes to AddOne is the result of operator* - not the iterator itself.
About for_each and a modifying function object - read this question
If in doubt use std::transform as it will convey even to the casual reader of your code that you intend to modify something.
It is legal - the input iterators are used to specify the range, not to do the processing.
Interestingly, Josuttis in his book "The C++ Standard Library" lists for_each as modifying, raher than non-modifying.
My suggestion would be, it all matters how you pass your item to the function i.e. whether by reference or pointer and change it or as a copy and change it.
But as other's have said, if you want to change the values, it's better to use transform as it cares about the return values.
class MultiplyBy {
private:
int factor;
public:
MultiplyBy(int x) : factor(x) {
}
int operator () (int other) const {
other = factor + other;
return other;
}
// int operator () (int & other) const {
// other = factor + other;
// return other;
// }
};
vector<int> x1= {1, 2, 3, 4, 5};
vector<int> x2;
std::transform(x1.begin(), x1.end(), back_inserter(x2), MultiplyBy(3));
std::for_each(x1.begin(), x1.end(), MultiplyBy(3));