Here is an exercise from C++ Primer 5th Edition:
Exercise 14.26: Define subscript operators for your StrVec, String,
StrBlob, and StrBlobPtr classes.(P.566)
The class StrVec compiled without any error nor warning.Below is the class body:
/**
* #brief The StrVec class a std::vector like class without template
* std:string is the only type it holds.
*/
class StrVec
{
public:
//! default constructor
StrVec():
element(nullptr), first_free(nullptr), cap(nullptr){}
// etc
//! public members
std::string& operator [](std::size_t n) {return element[n];}
const std::string& operator [](std::size_t n) const {return element[n];}
// ^^^^^
// etc
private:
//! data members
std::string* element; // pointer to the first element
std::string* first_free; // pointer to the first free element
std::string* cap; // pointer to one past the end
std::allocator<std::string> alloc;
// etc
};
When compiling the class String, a warning was generated, as shown below:
/**
* #brief std::string like class without template
*
* design:
*
* [0][1][2][3][unconstructed chars][unallocated memory]
* ^ ^ ^
* elements first_free cap
*/
class String
{
public:
//! default constructor
String();
// etc
char operator [](std::size_t n) {return elements[n];}
const char operator [](std::size_t n) const {return elements[n];}
// ^^^^^
private:
//! data members
char* elements;
char* first_free;
char* cap;
std::allocator<char> alloc;
// etc
};
Warning from the compiler:
warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
const char operator [](std::size_t n) const {return elements[n];}
^
The compiler I was using:
gcc version 4.8.1 (Ubuntu 4.8.1-2ubuntu1~13.04)
Why is it so? Is there any significant difference between the two classes?
const char operator [](std::size_t n) const {return elements[n];}
This returns a const copy of elements[n], which is no use at all. You return a const when you don't want the caller changing your stuff, but since you're returning a copy here, you wouldn't be changing anything anyways.
Your first example is returning a const reference, which is what you should do here.
The first version is returning a reference to an array element. Whether or not this is a const reference determines whether you can just read the element's value or write to the element also.
The second version is returning a copy of an array element. If this is deliberate, you only need
char operator [](std::size_t n) const {return elements[n];}
If you wanted two overloads of operator [], one that allows an element to be read and another that allows it to be written, you need to return references instead
char& operator [](std::size_t n) {return elements[n];}
const char& operator [](std::size_t n) const {return elements[n];}
Related
class Vector{
......
.......
private:
int dim;
public:
int getDim() {
return this->dim;
}
const Vector operator+(const Vector& right){
this->getSize();
}
};
And I got compile error in this->getSize();. It is caused fact, that argument right is const. I don't know where is problem. I don't try modify right.
Presumably you have a non-const method Vector::getSize(). You need to make it const so that it can be called on const objects or via const references or pointers to const. For example:
int getSize() const;
^^^^^
Also note that it doesn't make much sense to return a const value (and would inhibit move semantics if you had them). The canonical form of an addition member operator would be
// const method: A = B + C should not modify B
Vector operator+(const Vector& right) const;
^^^^^
and the non-member
Vector operator+(const Vector& left, const Vector& right);
It's an exercise from C++ Primer 5th Edition:
Exercise 14.7: Define an output operator for you String class you
wrote for the exercises in ยง 13.5 (p. 531).(Page 558)
The string.h I wrote for previous exercises:
/**
* #brief std::string like class without template
*
* design:
*
* [0][1][2][3][unconstructed chars][unallocated memory]
* ^ ^ ^
* elements first_free cap
*/
class String
{
friend std::ostream& operator <<(std::ostream& os, const String& s);
public:
//! default constructor
String();
//! constructor taking C-style string i.e. a char array terminated with'\0'.
explicit String(const char * const c);
//! copy constructor
explicit String(const String& s);
//! move constructor --07.Jan.2014
String(String&& s) noexcept;
//! operator =
String& operator = (const String& rhs);
//! move operator = --07.Jan.2014
String& operator = (String&& rhs) noexcept;
//! destructor
~String();
//! members
char* begin() const { return elements; }
char* end() const { return first_free; }
std::size_t size() const {return first_free - elements; }
std::size_t capacity() const {return cap - elements; }
private:
//! data members
char* elements;
char* first_free;
char* cap;
std::allocator<char> alloc;
//! utillities for big 3
void free();
};
std::ostream&
operator << (std::ostream& os, const String& s);
Part of the string.cpp:
//! constructor taking C-style string i.e. a char array terminated with'\0'.
String::String(const char * const c)
{
auto p = c;
char* newData = alloc.allocate(sizeof(p));
std::uninitialized_copy(p, (p + sizeof(p)), newData);
//! build the data structure
elements = newData;
cap = first_free = newData + sizeof(c);
}
std::ostream &operator <<(std::ostream &os, const String &s)
{
std::for_each(&s.elements, &s.first_free, [&](const char* p){
os << *p;
});
return os;
}
main.cpp:
#include "string.h"
#include <iostream>
int main()
{
String s("1234");
std::cout << s <<"\n";
return 0;
}
Output:
1
Press <RETURN> to close this window...
Why is the output like so? why not 1234?
Probably because elements points to an array of char, so each element is a char, not a char*.
You also need to drop the & in front of s.elements and s.first_free, because you are interested in the addresses the pointers point to, not the addresses of the pointers themselves.
So, this code would work:
std::for_each(s.elements, s.first_free, [&](char p){
os << p;
});
As mentioned by #TemplateRex in comments, it would be both cleaner and more idiomatic to use the begin() and end() member functions:
std::for_each(s.begin(), s.end(), [&](char p){ os << p; });
sizeof(pointer) where pointer is a char const* does not return the length of an array. You make this mistake multiple times. Use strlen instead. This is hidden because your string is 4 char long, and on a 32 bit system sizeof(ptr) is 4.
Next &first_free and similar in your for_each should be just first_free.
Next your lambda should take char not char*s. Then the output should be << p not << *p.
You should create both const and non-const begin and end. const returns char const *, while non const returns char* -- containers that logically own their underlying data should use const that way for iteration.
Next replace your for_eaxh to use begin() and end() like for_each( x.begin(), x.end(), ... -- no need to redo what begin and end do. In C++11 you can even use a ranged based for:
for(char c : s ) {
std::cout << c
}
instead of for_each.
I'm trying to create a set that can be filled with instances of a class. I thought that a requirement for a set is that elements can be compared, and thus in this case I need to tell my class how they can be compared. I created the following class:
class Myclass
{
public:
Myclass(int i):storedval(i) {}
int getval(){return storedval;}
friend bool operator> (Myclass &first, Myclass &second)
{
return first.getval()>second.getval();
}
friend bool operator< (Myclass &first, Myclass &second)
{
return first.getval()<second.getval();
}
private:
int storedval;
};
But when I try to add instances to a set like this:
Myclass a(50);
set<Myclass> Bunchofclasses;
Bunchofclasses.insert(a);
It gives me a bunch of error text that I think tells me it doesn't understand the > and < operators. How should this be done properly?
You need to pass const references to the comparison function because it should not be allowed to modify the items being compared:
friend bool operator< (const Myclass& first, const Myclass& second)
{ // ^^^^^ ^^^^^
return first.getval() < second.getval();
}
This will require that you make getVal() const as well. This makes it callable on const instances or via const references:
int getval() const {return storedval;}
// ^^^^^
Note that you do not need operator> for the set to work. Less-than is enough.
Strictly speaking, you do not need a friend function here, since you are calling public member functions. The only thing that friend brings here is to allow you to declare a non-member function inside of the class definition.
Elements of a set are immutable, so the set needs to be able to compare const values. So the comparison needs to be:
friend bool operator< (Myclass const &first, Myclass const &second)
// ^^^^^ ^^^^^
(You should probably also do this with operator> and any other non-mutating operations you provide; but set only needs operator<).
Also, in order to access the value of a const object, the accessor also needs to be const:
int getval() const
// ^^^^^
A question related to a custom Vector class in C++.
template <typename T>
class Vector
{ ...
private:
T * mData; int mSize;
public:
proxy_element operator[](const size_type index) { return proxy_element(*this, index); }
const T& operator[](const size_type index) const { return mData[index]; }
};
template <typename T>
class proxy_element
{ ...
proxy_element(Vector<T>& m_parent, const size_type index);
proxy_elem& operator=(const T& rhs); // modifies data so invalidate on other memories
bool operator==(const proxy_elem& rhs) // only read, just copy data back.
...
}
The reason for using proxy_element class is to distinguish and optimize read and writes operations, considering that the vector data can reside in GPU device memories as well. So any read operation require only to copy latest data back (if any) but a readwrite/write operation require invalidating data in device memories.
This design work well when the element type is primitive. However for more complex element types, there is one issue:
struct person{ int age; double salary; };
int main()
{
Vector<person> v1(10);
v[1].age = 10; // gives error as operator[] returns proxy_element for which "." operator has no meaning
}
AFAIK, the "." operator cannot be overload in C++. One obvious solution is to not use proxy_elem and just return regular reference (T &), assuming that each access is a write access, but that will be inefficient for obvious reasons.
Is there any other work around which gives me "." operator working while retaining ability to distinguish between read and write operations?
One option is to make such data types immutable (private member variables, initialised by a constructor, and the only setter is the class's assignment operator). This way, the only means to change anything is to assign to an entire instance of the class, which can be channeled through a proxy_element.
Marcelo Cantos's answer is, of course, the proper way to do things. However, there is the complicated and crazy workaround of specialization. (Not recommended.)
//if it's a class, inherit from it to get public members
template<class T>
class proxy_element : public T {
...
proxy_element(Vector<T>& m_parent, const size_type index);
proxy_elem& operator=(const T& rhs); // modifies data so invalidate on other memories
bool operator==(const proxy_elem& rhs) // only read, just copy data back.
...
};
//pretend to be a pointer
template<>
class proxy_element<T*> {
...
proxy_element(Vector<T>& m_parent, const size_type index);
proxy_elem& operator=(const T& rhs); // modifies data so invalidate on other memories
bool operator==(const proxy_elem& rhs) // only read, just copy data back.
...
};
//otherwise, pretend to be primitive
#define primitive_proxy(T) \
template<> class proxy_element {
...
proxy_element(Vector<T>& m_parent, const size_type index);
proxy_elem& operator=(const T& rhs); // modifies data so invalidate on other memories
bool operator==(const proxy_elem& rhs) // only read, just copy data back.
...
};
primitive_proxy(char)
primitive_proxy(unsigned char)
primitive_proxy(signed char) //this is distinct from char remember
primitive_proxy(short)
primitive_proxy(unsigned short)
primitive_proxy(int)
primitive_proxy(unsigned int)
primitive_proxy(long)
primitive_proxy(unsigned long)
primitive_proxy(long long)
primitive_proxy(unsigned long long)
primitive_proxy(char16_t) //if GCC
primitive_proxy(char32_t) //if GCC
primitive_proxy(wchar_t)
primitive_proxy(float)
primitive_proxy(double)
primitive_proxy(long double)
Let's say I have something like the following method in my container class:
Datatype& operator[](const unsigned int Index) // I know this should use size_t instead.
{
return *(BasePointer + Index); // Where BasePointer is the start of the array.
}
I'd like to implement some sort of bounds-checking for the MyInstance[Index] = Value usage so the container resizes automatically if the user tries to change a value outside its range. However, I want something else to happen if the user tries to access a value outside the container's range, e.g. MyVariable = MyInstance[Index]. How can I detect how operator[] is being used?
Sketch:
return a proxy object instead of the actual data entry. The proxy object then defines operator = to handle the assignment case, and an implicit conversion operator for the reading-out case.
template <typename T>
class AccessorProxy {
friend class Container<T>;
public:
AccessorProxy(Container<T>& data, unsigned index)
: data(data), index(index) { }
void operator =(T const& new_value) {
// Expand array.
}
operator const T&() const {
// Do bounds check.
return *(data.inner_array + index);
}
private:
AccessorProxy(const AccessorProxy& rhs)
: data(rhs.data), index(rhs.index) {}
AccessorProxy& operator=(const AccessorProxy&);
Container<T>& data;
unsigned index;
};
template <typename T>
class ConstAccessorProxy {
friend class Container<T>;
public:
ConstAccessorProxy(const Container<T>& data, unsigned index)
: data(data), index(index) { }
operator const T&() const {
// Do bounds check.
return *(data.inner_array + index);
}
private:
ConstAccessorProxy(const ConstAccessorProxy& rhs)
: data(rhs.data), index(rhs.index) {}
ConstAccessorProxy& operator=(const ConstAccessorProxy&);
const Container<T>& data;
unsigned index;
};
AccessorProxy<Datatype> operator[](const unsigned int Index)
{
return AccessorProxy<Datatype>(*this, Index);
}
ConstAccessorProxy<Datatype> operator[] const (const unsigned int Index)
{
return ConstAccessorProxy<Datatype>(*this, Index);
}
The accessor classes will likely need to be be friends of the container class.
Finding ways to avoid the code duplication is left as an exercise to the reader. :)
Use a dummy class type to represent expressions like MyInstance[Index] and delay figuring out what to do until that expression is used.
class MyContainer {
private:
class IndexExpr {
public:
// Get data from container:
operator const Datatype&() const;
// Expand container if necessary, then store data:
Datatype& operator=(const Datatype& value);
// Treat MyInstance[i] = MyInstance[j]; as expected:
Datatype& operator=(const IndexExpr& rhs)
{ return *this = static_cast<const Datatype&>(rhs); }
private:
IndexExpr(MyContainer& cont, unsigned int ind);
MyContainer& container_;
unsigned int index_;
friend class MyContainer;
};
public:
IndexExpr operator[](unsigned int Index)
{ return IndexExpr(*this, Index); }
// No IndexExpr needed when container is const:
const Datatype& operator[](unsigned int Index) const;
// ...
};
This is not a perfect answer to "how to detect", but, if the user is accessing the operator[] via a const instance, then throw an exception if the index is out of bounds.
i.e.
Datatype const& operator[]() const { .. // don't modify here, throw exception
However, if the user is accessing the instance via a non const instance, then by all means expand if the index is out of bounds (and within your acceptable ranges)
Datatype& operator[]() { .. // modify here
Basically, you are using the const attribute of the instance to determine what your semantics would be (as done in std::map - i.e. trying to call operator[] on a const instance of a map results in a compiler error - i.e. there is no const qualified operator[] for map, because the function is guaranteed to create a mapping if the key does not exist already.)