Re: Help with constness and output operator<<
marco.guazzone@gmail.com wrote:
Dear all,
Suppose you have a class, say "foobar", which let you store elements
in an array of predefined size, such that:
* only elements at even position are assignable [i.e., foo(i) = value
if (i % 2 == 0)]
* elements at odd position are set automatically to zero [i.e., foo(i)
= 0 if (i % 2 == 0)]
To do this I have two methods of foobar:
--- [snip] ---
template <typename T>
class foobar
{
...
reference operator()(std::size_t i)
{
if (!(i % 2))
{
return v_[i];
}
throw std::runtime_error("Not assignable");
}
const_reference operator()(std::size_t i) const
{
if (!(i % 2))
{
return v_[i];
}
return zero_; // The constant 0
}
};
--- [/snip] ---
The problem is that the "const" method is not called in the output
operator <<.
In fact when I execute the line:
std::cout << foo(1) << std::endl
Instead of getting zero as output (resulting from the call to the
const method) I get the exception "Not assignable".
To make it to work I have to explicitly cast to type "foobar const&".
Where I am wrong?
You are wrong in thinking that the wider context where the
operator/function is used affects which overload is chosen.
For choosing between the const and non-const version, the only
consideration is if the operator/function is invoked on a const or non-
const object.
As foo is non-const, the non-const operator() overload is chosen in the
output statement.
To achieve the results you want, you need to use a proxy-object that can
detect the actual assignment. See below how this can be done.
[note: just for info, I'm using the GCC compiler 4.4.1 with flags -
Wall -Wextra -pedantic -ansi]
Here below is the entire code:
--- [code] ---
#include <cstddef>
#include <iostream>
#include <stdexcept>
template <typename T>
struct foobar
{
typedef T value_type;
typedef T& reference;
typedef T const& const_reference;
foobar(std::size_t n)
: v_(new value_type[n/2])
{
}
~foobar() { delete[] v_; }
private:
struct proxy
{
proxy(value_type*& v, std::size_t i, const T& zero):
v_(v), index(i), zero_(zero) {}
operator const T&()
{
if (!(index % 2))
{
return v_[index];
}
return zero_;
}
T& operator=(const T& val)
{
if (!(index % 2))
{
v_[index] = val;
return v_[index];
}
else
{
throw std::runtime_error("Not assignable");
}
}
private:
T*& v_;
const T& zero_;
std::size_t index;
};
public:
proxy operator()(std::size_t i)
{
return proxy(v_, i, this->zero_);
}
const_reference operator()(std::size_t i) const
{
if (!(i % 2))
{
return v_[i];
}
return this->zero_;
}
private: value_type* v_;
private: static const value_type zero_;
};
template <typename T>
const T foobar<T>::zero_ = 0;
int main()
{
const std::size_t N = 5;
foobar<int> foo(N);
std::cout << "Populating..." << std::endl;;
for (std::size_t i=0; i < N; i += 2)
{
foo(i) = i+1;
}
std::cout << "Querying..." << std::endl;;
for (std::size_t i=0; i < N; ++i)
{
std::cout << "foo(" << i << ") ==> " << std::flush;
std::cout << foo(i) << std::endl; // DON'T WORK
//std::cout << (static_cast<foobar<int> const&>(foo))(i) <<
std::endl; // WORK
}
}
--- [/code] ---
Thank you very much for any advice
Best,
-- Marco
Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]