I'm tasked with improving C arrays by implementing an array class with various functionality. I'm trying to overload '[]' but I've ran into an issue.
template<typename T>
class SA{
private:
T* pT;
//other vars...
public:
//other initialization functions...
int& operator [](int offset);
};
template<typename T>
int& SA<T>::operator [](int offset){
if (offset < lIdx || offset > hIdx){
std::cout << "invalid index:" << std::endl;
}
return pT[offset];
}
int main(){
SA<float> thing1(15);
thing1[5] = 5;
return 0;
When I use this overloading function, I'm met with this error:
error: cannot bind non-const lvalue reference of type ‘int&’ to an
rvalue of type ‘int’
However, if I understand correctly I can't re-assign the value at this index with '=' if I remove the '&'. The compiler will also throw this error if I do so:
error: lvalue required as left operand of assignment
What am I supposed to do here?
The return type of the function should be T&, not int&:
T& operator [](int offset);
Also note that, while it is nice that you check to make sure the index is in range, you don't actually do anything about it. One solution is to throw an exception:
#include <stdexcept>
template <typename T>
T& SA<T>::operator[](int offset) {
if (offset < lIdx || offset > hIdx)
throw std::out_of_range{"Array index out of range"};
return pT[offset];
}
If you really want to be consistent with STL containers, let operator[] be unchecked, and add a debug-time check like:
#include <cassert>
template <typename T>
T& SA<T>::operator[](int offset) noexcept { // note noexcept
assert(offset < lIdx || offset > hIdx);
return pT[offset];
}
Adding a const-overload of this operator would also be nice:
const T& operator [](int offset) const noexcept;
first you should adjust the return type according to your value type:
template<typename T>
T& SA<T>::operator [](int offset){
if (offset < lIdx || offset > hIdx){
std::cout << "invalid index:" << std::endl;
}
return pT[offset];
}
Then if you want it to also work in constant mode add the following overload:
template<typename T>
const T& SA<T>::operator [](int offset)const{
if (offset < lIdx || offset > hIdx){
std::cout << "invalid index:" << std::endl;
}
return pT[offset];
}
Related
I have my class Table below. Pay attention at the way I have implemented operator[]. The reason i have used decltype(auto) as a return type is because (to my knowledge at least) when you would apply your operator twice in row (eg. my_table[i][j] ), the returned types are different: after first application it would be a vector& after the second consecutive application it would be T. It works fine and well but to my surprise if I just rewrite operator[] as follows it still works fine:
vector<T>& operator[](size_t input) const{
return data[input];
}
The question is WHY? how does it return the required reference to the requested element in t[i][j]?
Original code:
template <typename T>
class Table{
public:
Table(size_t r_num, size_t c_num) : data(r_num , vector<T> (c_num, T{})) {}
decltype(auto) operator[](size_t input) const{
return data[input];
}
decltype(auto) operator[](size_t input){
return data[input];
}
void Resize(size_t r_num, size_t c_num){
data.resize(r_num);
for(auto& it : data)
it.resize(c_num);
}
pair<size_t,size_t> Size() const{
if(data.empty() || data[0].empty())
return pair{0, 0};
return pair{data.size(), data[0].size()};
}
private:
vector<vector<T>> data;
};
my_table[i][j] = 3 is essentially the same thing as:
auto& tmp = my_table[i];
tmp[j] = 3;
Taking that into consideration, both of these are actually 100% equivalent:
vector<T>& operator[](size_t input) {
return data[input];
}
// same thing as
decltype(auto) operator[](size_t input) {
return data[input];
}
They both return a std::vector<int>&, and the second indexing operation applies to that vector.
So why do you need to use decltype(auto) then? It's because auto strips out references:
auto operator[](size_t input) {
return data[input];
}
// same thing as
vector<T> operator[](size_t input) {
return data[input];
}
Which returns a copy of the row, and would prevent assignment.
We have:
Table t;
t[i][j] = 7;
While the first operator[] applied is overloaded in class Table(returns: vector<T>&), the second operator[] would apply to the vector<T>& and thus its already an operator that is overloaded for class vector not class table.
I wrote an expression template code, but I had an error.
And the following is just a minimal reproducible example.
#include <iostream>
template<typename Derived>
class VecExp {
public:
double operator[](int i) const {return static_cast<Derived const&>(*this)[i];}
};
template<int n>
class Vector : public VecExp<Vector<n>> {
public:
template<int m>
Vector(const double(&arr)[m]) {
static_assert(m==n, "");
for(int i=0; i<m; ++i) data[i] = arr[i];
}
template<typename E>
Vector(const VecExp<E>& exp) {
for(int i=0; i<n; ++i) data[i] = exp[i];
}
double operator[](int i) const {std::cout<<this<<std::endl; return data[i];}
double& operator[](int i) {return data[i];}
double data[n];
};
template<typename E1, typename E2>
class VectorSum : public VecExp<VectorSum<E1, E2>>{
E1 const& lhs;
E2 const& rhs;
public:
VectorSum(E1 const& lhs, E2 const& rhs) : lhs(lhs), rhs(rhs) {
// std::cout << &(this->lhs) << ", " << &(this->rhs) << std::endl;
}
double operator[](int i) const {return lhs[i] + rhs[i];}
};
template<typename E1, typename E2>
VectorSum<E1, E2>
operator + (VecExp<E1> const& lhs, VecExp<E2> const& rhs) {
return VectorSum<E1, E2>(*static_cast<const E1*>(&lhs), *static_cast<const E2*>(&rhs));
}
int main() {
Vector<16> v({1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16});
std::cout << &v << std::endl;
const auto sum = v + v + v + v;
Vector<16> v2 = sum; // error at here
std::cout << sum[0] << std::endl;
return 0;
}
When I try to use VectorSum<...>(sum in main()) that adds more than 3 Vectors, the following error happens in a release build.
Exception 0xc0000005 encountered at address 0x7ff76f8a1189: Access violation reading location 0x00000000
So I attached the pointer printer like above, and then it says
00000067C1EFF920
0000000000000000
Process finished with exit code -1073741819 (0xC0000005)
If I attach more printers(i.e: in operator + or VecSum ctor, ...), then suddenly everything works fine.
Can anyone please tell me why is this error happening?
Used CLion on Windows, Visual Studio community 2019 installed.
v + v is an expression of type VectorSum<Vector<16>, Vector<16>>.
v + v + v is an expression of type VectorSum<VectorSum<Vector<16>, Vector<16>>, Vector<16>>, where the type for the first template parameter is significant:
VectorSum<VectorSum<Vector<16>, Vector<16>>, Vector<16>>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (B)
// ^^^^^^^^^ (A)
The object associated with this specialization, A, stores a const reference of type B const&, but once the expression v + v + v expires so does the lifetime of the sub-expression v + v, meaning you are storing a dangling reference in the expression object (A) (as the data member lhs).
The reason you do not see this problem when just storing the expression template for the expression v + v is that both the lhs and rhs data members of the associated specialization object refers to the object v whose lifetime extends to the end of main().
Finally, be careful when storing expression templates in intermediate variables using placeholder types such as auto, as it will 1) be likely to yield lifetime issues such as the one above, and 2) is likely to be unexpected to the casual reader, as one could expect (compare with std::vector<bool>::operator[])) auto = v + v to result in decltype(v) (namely a Vector<16>) rather than a proxy type that should for all means be hidden from the client.
I am using std::bitset with enum class to have more convenient tool. But I am facing a compilation error when returning value from operator[]:
error: non-const lvalue reference to type 'bool' cannot bind to a temporary of type 'std::__1::bitset<2>::reference' (aka '__bit_reference<std::__1::__bitset<1, 2> >')
I have to return it via reference to be able to assign a value to it. Here is my full code:
template<typename T>
struct EnumTraits;
template<typename T>
class EnumClassBitset
{
private:
std::bitset<static_cast<typename std::underlying_type<T>::type>(EnumTraits<T>::max)> m_bitset;
typename std::underlying_type<T>::type get_value(T v) const
{
return static_cast<typename std::underlying_type<T>::type>(v);
}
public:
bool& operator[](T pos)
{
return m_bitset[get_value(pos)];
}
bool test(T pos) const
{
return m_bitset.test(get_value(pos));
}
EnumClassBitset& reset(T pos)
{
m_bitset.reset(get_value(pos));
return *this;
}
EnumClassBitset& flip(T pos)
{
m_bitset.flip(get_value(pos));
return *this;
}
};
enum class BitFlags
{
Write,
Read,
NumOfFlags
};
template<>
struct EnumTraits<BitFlags>
{
static const BitFlags max = BitFlags::NumOfFlags;
};
Here is how I am trying to use it:
EnumClassBitset<BitFlags> m_flags;
m_flags[BitFlags::Write] = true;
cout << "Write flag: " << m_flags[BitFlags::Write] << endl;
Appreciate any help, thanks in advance.
std::bitset (like std::vector<bool>) does not return a bool & from the non-const-version of operator[]. The reasons for this are technical and come from the fact that a bool variable is one byte big while a element of std::bitset is only one bit big. Further bool as type has an alignment requirement of 1 byte, but the single bits in std::bitset are unaligned. So the normal bool & cannot reference to such an element.
That's why the std::bitset::operator[] returns a proxy object of type std::bitset::reference. You probably have to forward this type instead of returning bool &.
I've seen questions similar to this, but haven't found a solution that worked on my problem, so I was hoping I could get some help.
I want to pass a class as an argument to a function with template parameters (For this example I just want it to compile, so nvm that it doesn't make sense with return true in operators). I figured this way in the following code would work:
template<typename T>
class MyClass {
public:
bool operator>(const T&) const {
return true;
}
bool operator<(const T&) const {
return true;
}
};
template<typename T, typename S>
vector<T> between_interval(T* start, T* end, const S& low, const S& high){
vector<T> vec;
for ( ; start != end; start++) {
if (low < *start && *start < high)
vec.push_back(* start);
}
return vec;
}
int main() {
double v1[] = {1.23, 4.56, 7.89, -10, 4};
MyClass<double> k1;
MyClass<double> k2;
vector<double> res = between_interval(v1, v1 + 3, k1, k2);
for (auto x : res)
cout << x << " ";
cout << endl;
}
I get the following error(s):
u4.cpp: In instantiation of ‘std::vector<T> between_interval(T*, T*, const S&, const S&) [with T = double; S = MyClass<double>]’:
u4.cpp:39:55: required from here
u4.cpp:28:36: error: no match for ‘operator<’ (operand types are ‘double’ and ‘const MyClass<double>’)
if (low < *start && *start < high)
I realize passing K1 and K2 won't make sense as it is at the moment, but the compiler doesn't mention this, it complains about not having a match for operator<, so my main question is why? Is it too general with a template argument in operator<? Is there any other way to make the operators work but without using the templates?
Thank you
With
template<typename T>
class MyClass {
public:
bool operator>(const T&) const {
return true;
}
bool operator<(const T&) const {
return true;
}
};
and
MyClass <double> k1;
double d = 4.2;
you might do
k1 < d
k1 > d
but you do
d < k1 (with *start < high)
you have to implement that operator too or change the code to use the provided operator.
Because your operator < is a member function it expects the left hand operand to be of the class type. That is fine for low < *start but in *start < high you have a double as the left hand operand.
You will either need to provide a free function that takes a double as the first parameter or make your class convertible to a double (or T).
Member operators always use the left operand as this and the right operand as the operator argument. That means your comparison operators will only work when the MyClass is on the left of the comparison. The quick fix is to change the line if (low < *start && *start < high) to if (low < *start && high > *start) to put the MyClass instance on the left side of each comparison. The cleaner solution is to provide free operators which take a T on the left side and a MyClass<T> on the right side. For example :
template<typename T>
bool operator>(const T& p_left, const MyClass<T> & p_right) {
return p_right < p_left;
}
template<typename T>
bool operator<(const T& p_left, const MyClass<T> & p_right) {
return p_right > p_left;
}
I have a class T, defined a destructor of T and try to define the + operator.
How do I suppose to delete t2?
or should I return the value of T from the function in another way?
T& T::operator + (const T& t1)
{
T* t2 = new T;
t2 = this + t1;
return *t2;
}
void main()
{
T t1(1,2), t2(3,8);
cout << (t1 + t2) << endl;
}
any help appreciated!
You don't need pointers here. Use objects. The usual idiom is to provide operator+= as a member function and operator+ as a free function:
class T {
public:
T& operator+=(const T& t) {
// do whatever you need to do to add `t` to `*this`
return *this;
}
T operator+(const T& lhs, const T& rhs) {
return T(lhs) += rhs;
}
You don't want to do this (probably at all).
What you want to do is create a temporary containing the correct value, and return that:
T T::operator+(const T& t1) const {
return value + t1.value;
}
For the typical case (where you want to allow conversions of the left operand) you probably want to use a free function:
T operator+(T const &a, T const &b) {
return T(a.val + b.val);
}
Note that unless T::val is public (usually a poor idea) this will probably need to be a friend of T.