Can anyone explain to me why the following code works:
#include <iostream>
class Vec
{
int *_vec;
unsigned int _size;
public:
Vec (unsigned int size) : _vec (new int [size]), _size(size) {};
int & operator[] (const int & i)
{
return _vec[i];
}
int & operator[] (const int & i) const
{
return _vec[i];
}
};
int main ()
{
const Vec v (3);
v[1] = 15;
std::cout << v[1] << std::endl;
}
It compiles and runs just fine, even though we're changing the contents of a const object. How is that okay?
The constness is with regards to the members of the class. You cannot change the value of v._vec, but there's no problem changing the content of the memory that v._vec points to.
Related
In the following code I made a template class, Its initialized in main function and I'm trying to assign char* as you can see below but It isn't working. I think the issue is in assign operator function I defined in Proxy class but I can't figure it out
#include <iostream>
using namespace std;
template <class T>
class Vector {
public:
T *p;
Vector(int size) {
p = new T[size];
}
class Proxy {
Vector &a;
int i;
public:
Proxy(Vector &a, int i) : a(a), i(i) {
}
void operator=(const T x) {
a.p[i] = x;
}
};
Proxy operator[](int i) {
return Proxy(*this, i);
}
};
int main() {
Vector<char *> sv1(2);
sv1[0] = "John";
sv1[1] = "Doe";
}
I'm getting following error;
I already tried setting parameter in assignment operator function to const, I also tried implicitly typecasting to T nothing has worked
Try this:
using namespace std;
template <class T>
class Vector {
public:
T* p;
int sz;
Vector(int size) {
p = new T[size];
sz = size;
}
template<class T>
class Proxy {
Vector<T>& v;
int i;
public:
Proxy(Vector<T>& vec, int index) :v(vec),i(index) { }
void operator= (const T val) { v.p[i] = val; }
};
Proxy<T> operator[](int index) { return Proxy<T>(*this, index); }
};
Your code will work with any basic type, (int, char, double) and pointers, but not, for example, with this:
int main() {
Vector<char*> sv1(2);
sv1[0] = "John";
sv1[1] = "Doe";
}
Firstly, the Vector points to a char*, not a string literal (const char*). You'd have to cast it using a C-style cast or a const_cast. Example:
int main() {
Vector<char*> sv1(2);
sv1[0] = const_cast<char*>("John"); //succeeds
sv1[1] = (char*)"Doe"; //succeeds
sv1[0] = "John"; //fails
sv1[1] = "Doe"; //fails
}
A string literal is always a const char* in C++.
You'll have same error writing code:
char * whatever = "something";
This code is absolutely wrong at least for string:
void operator=(const T x)
{
a.p[i] = x;
}
Step 1: allocate buffer;
Step 2: copy string to allocated buffer.
Your code is OK for primitives like char, int, etc. The following code should work:
int main() {
Vector<char> sv1(2);
sv1[0] = 'J';
sv1[1] = 'D';
}
I am running into this error...
...with this minimal executable code to reproduce the error and ...
#include <iostream>
class arrayClass
{
private:
int _elements;
int _array[];
public:
arrayClass()
: _elements(32)
{
//If i is smaller than 2 the "Stack Smashing Detected" error shows up, why is that?
//If i is 2 or more, no error appears
//(f.e. int i = 0 or int i = 1 doesn't work, int i = 2 or higher works - why is that?)
for(int i = 0; i < _elements; ++i){
_array[i] = 0;
}
}
int get(int index){
return _array[index];
}
};
int main()
{
arrayClass arr;
std::cout << arr.get(2) << std::endl;
return 0;
}
...it doesn't matter if I initiate _elements with the initialization list or at with the
attribute itself with f.e.32 or whatever number.
If I pass the int value to construct the arrayClass(int) in addition to the arrayClass() constructor, the error doesn't shows up.
If I construct the arrayClas(int) with a value alone, it can also just be used with the 2nd slot upwards.
So my question is:
Why couldn't I initiate the 1st and 2nd array slot of a default array[]?
Or the other way around, why is it possible to assign an empty array[] to a class without a value and not f.e. _array[32] without an error but the one with assigning array[0] = 0; and array[1] = 0; ?
(And yes, I am aware of vectors, I need to use arrays for various reasons)
Because you never allocate memory for the array to begin with, everything is undefined behavior. I don't even know what int _array[] evaluates to without a size specifier. I'll look that up later.
Change your construct code to "new" the array. And have a destructor to delete it.
class arrayClass
{
private:
int _elements;
int* _array;
public:
arrayClass()
: _elements(32)
{
_array = new int[_elements];
memset(_array, '\0', sizeof(array[0])*_elements);
}
int get(int index){
return _array[index];
}
~arrayClass()
{
delete [] _array;
}
};
Or if you can have a fixed number of elements, explictly size the array when it's declared:
class arrayClass
{
private:
int _array[32];
public:
arrayClass()
: _array() // this will zero-init the array
{
}
int get(int index){
return _array[index];
}
};
int _array[]; is a flexible array and isn't allowed in standard C++. It does not allocate memory so when you access any element in the array, you have undefined behavior.
I am aware of vectors, I need to use arrays for various reasons
In reality there are very few valid reasons so I expect the various reasons you mention to be artificial. If you need to pass the data to a function, like void func(int*, size_t elements);, you can still use a std::vector<int>. Just pass its data() and size() as arguments.
In C++ you should typically use a std::vector<int> for cases like this.
Example:
#include <iostream>
#include <vector>
class arrayClass
{
private:
std::vector<int> _array;
public:
arrayClass(size_t s = 32)
: _array(s)
{}
size_t size() const {
return _array.size();
}
int get(size_t index) const {
return _array[index];
}
};
int main()
{
arrayClass arr;
std::cout << arr.get(10) << std::endl;
return 0;
}
An alternative, if your arrayClass has a fixed number of elements:
#include <array>
class arrayClass
{
private:
std::array<int, 32> _array;
public:
arrayClass()
: _array{}
{}
size_t size() const {
return _array.size();
}
int get(size_t index){
return _array[index];
}
};
If the extra space a std::vector consumes (usually 4 or 8 bytes) is a real concern, you could make a similar class that only stores the pointer to the allocated memory and the size. It could look like this (but doesn't have the ability to grow/shrink like a vector has):
#include <iostream>
#include <algorithm>
#include <memory>
#include <type_traits>
template<typename T, std::enable_if_t<std::is_default_constructible_v<T>>* = nullptr>
class arrayClass {
public:
using value_type = T;
arrayClass(size_t size = 32) :
_size(size),
_array(std::make_unique<T[]>(_size))
{}
// copy constructor
arrayClass(const arrayClass& rhs) :
_size(rhs._size),
_array(std::make_unique<T[]>(_size))
{
static_assert(std::is_copy_assignable_v<T>, "T must be copy assignable");
std::copy(rhs._array.get(), rhs._array.get() + _size, _array.get());
}
arrayClass(arrayClass&&) = default; // move constructor
// copy assignment operator
arrayClass& operator=(const arrayClass& rhs) {
*this = arrayClass(rhs); // copy construct and move assign
return *this;
}
arrayClass& operator=(arrayClass&&) = default; // move assignment operator
// accessing element at index
T& operator[](size_t index) { return _array[index]; }
const T& operator[](size_t index) const { return _array[index]; }
// bounds checking access to element
T& at(size_t idx) {
if(idx >= _size)
throw std::out_of_range(std::to_string(idx) + ">=" + std::to_string(_size));
return _array[idx];
}
const T& at(size_t idx) const {
if(idx >= _size)
throw std::out_of_range(std::to_string(idx) + ">=" + std::to_string(_size));
return _array[idx];
}
size_t size() const { return _size; }
// support for iterating over the elements in the array
const T* cbegin() const { return _array.get(); }
const T* cend() const { return _array.get() + _size; }
const T* begin() const { return cbegin(); }
const T* end() const { return cend(); }
T* begin() { return _array.get(); }
T* end() { return _array.get() + _size; }
private:
size_t _size;
std::unique_ptr<T[]> _array;
};
using intArray = arrayClass<int>;
int main() {
try {
intArray arr1(10);
// the added iterator support makes range-based for-loops possible:
for(int& v : arr1) {
static int i=0;
v = ++i;
}
intArray arr2;
arr2 = arr1; // copy assign
for(size_t i=0; i < arr2.size(); ++i) {
std::cout << arr2[i] << '\n'; // access using operator[] works too
}
std::cout << arr2.at(10) << '\n'; // throws out_of_range exception
}
catch(const std::out_of_range& ex) {
std::cerr << ex.what() << '\n';
}
}
Considering the following toy example, where I declare a class which encapsulates ublas from boost libraries:
#include <boost/numeric/ublas/matrix_sparse.hpp>
#include <iostream>
namespace ublas = boost::numeric::ublas;
class UblasEncapsulated {
public:
ublas::compressed_matrix<float>::reference operator()(int i, int j){
std::cout << "Non const reference" << std::endl;
MtrUpdated_ = true;
return mtr_(i, j);
}
ublas::compressed_matrix<float>::const_reference operator()(
int i, int j) const {
std::cout << "Const reference" << std::endl;
return mtr_(i, j);
}
UblasEncapsulated() { MtrUpdated = false; }
private:
ublas::compressed_matrix<float> mtr_(3, 3);
bool MtrUpdated_;
};
int main() {
UblasEncapsulated foo;
foo(2, 0) = 1.0f;
float const foo_float = foo(2, 0);
return 0;
}
I was expecting the output
Non constant reference
Constant reference
But I got
Non constant reference
Non constant reference
What am I doing wrong? How can I properly track when mtr_ could have its values changed?
foo is non-const, so the non-const version of foo.operator() will be called. It doesn't matter how the value it returns is used.
If you really want to know that MtrUpdated_ is only set true if an element is actually assigned to, you will need to use a proxy class:
class UblasEncapsulated {
public:
class proxy {
public:
proxy(UblasEncapsulated* ptr, int i, int j)
: ptr_(ptr), i_(i), j_(j)
{}
proxy& operator=(float f) {
ptr_->MtrUpdated_ = true;
ptr_->mtr_(i_, j_) = f;
return *this;
}
operator float() {
return ptr_->mtr_(i_, j_);
}
private:
UblasEncapsulated* ptr_;
int i_;
int j_;
};
proxy operator()(int i, int j) {
return proxy(this, i, j);
}
ublas::compressed_matrix<float>::const_reference operator() (int i, int j) const {
return mtr_(i, j);
}
UblasEncapsulated()
: mtr_(3, 3),
MtrUpdated_(false)
{}
private:
ublas::compressed_matrix<float> mtr_;
bool MtrUpdated_;
};
Live Demo
Note that you should avoid using a proxy class if you can get away with it since it doesn't play nicely with things like auto or template argument deduction.
Take a look at this simple array class
class Array {
const unsigned int _size;
int _array[100];
public:
Array() : _size(100) {
for(unsigned int i = 0; i < _size; i++)
_array[i] = 0;
}
int& operator[](unsigned int index) {
cout << "normal operator[].\n";
return _array[index];
}
const int& operator[](unsigned int index) const {
cout << "const operator[].\n";
return _array[index];
}
};
int main()
{
Array a;
a[3] = 1;
cout << a[3] << "\n";
system("pause");
return 0;
}
The "normal operator[]" line is executed twice, though I would expect the second call (cout << a[3] << "\n";) to be using the const version of the overloaded operator, because it doesn't change the array itself.
Why is that? Is there a way to force the const version to be called as I wish?
When you have an overloaded const version of a method, the const version will be called when the object is const. For example:
#include <iostream>
using namespace std;
class MyClass
{
public:
void foo()
{
cout << "foo()" << endl;
}
void foo() const
{
cout << "foo() const" << endl;
}
};
int main()
{
MyClass a;
const MyClass b;
a.foo();
b.foo();
return 0;
}
will call the normal foo() for the object a, and the const version for the object b.
In your case, you just have to avoid trying to assign to the const version. For example:
Array a;
const Array b;
a[3] = 1;
// b[3] = 1; // error
cout << a[3] << "\n";
cout << b[3] << "\n";
works fine. But if you try to make the assignment to b, you get a compile error.
std::ostream &operator<<(int x) doesn't take its parameter as const (because const isn't useful when passing by value), so the non-const operator[] can be called.
So, when will const operator[] be called?
It is true that a const vector variable declaration is almost always useless aside from some edge cases. The primary reason const operator[] is important, and the most often you will see it used, is calling it on a reference parameter.
int readStuff(const std::vector<int> &dontMutateMe) {
return dontMutateMe[42]; // const
}
Constant reference parameters are valuable, and this code wouldn't work without const operator[].
Is there a way in C++03 (or earlier) to write a class that can either store a const or non-const pointer, and handles access appropriately? Take the usage of the non-functional "SometimesConst" class as an example:
class SometimesConst
{
public:
SometimesConst(int * buffer) : buffer(buffer) {} // Needs const qualifier?
int* get() { return buffer; } // Needs const qualifier?
void increment() { counter++; }
private:
int * buffer; // Needs const qualifier?
int counter;
};
void function(int * n, const int * c)
{
// These are both okay
SometimesConst wn(n);
SometimesConst wc(c);
// Reading the value is always allowed
printf("%d %d", wn.get()[0], wc.get()[0]);
// Can increment either object's counter
wn.increment();
wc.increment();
// Can set non-const pointer
wn.get()[0] = 5;
// Should generate a compiler error
wc.get()[0] = 5;
}
Creating a const SometimesConst would not allow modification of the counter property of the object. Can a class be designed that has compile-time const safety for input objects, only if they are passed in as const?
No, not the way you are wanting to use it. The only way to have different behavior at compile time is to have different types. However, you can make that fairly easy to use:
#include <stdio.h>
template <typename T>
class SometimesConst
{
public:
SometimesConst(T* buffer) : buffer(buffer) { }
T* get() { return buffer; }
void increment() { ++counter; }
private:
T *buffer;
int counter;
};
typedef SometimesConst<const int> IsConst;
typedef SometimesConst<int> IsNotConst;
void function(int * n, const int * c)
{
IsNotConst wn(n);
IsConst wc(c);
// Reading the value is always allowed
printf("%d %d", wn.get()[0], wc.get()[0]);
// Can increment either object's counter
wn.increment();
wc.increment();
// Can set non-const pointer
wn.get()[0] = 5;
// Should generate a compiler error
wc.get()[0] = 5;
}
The language already mostly lets you do this with a simple class; with the way const cascades to access to members (combined with mutable for the counter member, which you've indicated should always be mutable), you can provide both read-only and read-write access to a buffer quite easily:
class C
{
public:
C(int* buffer) : buffer(buffer) {}
const int* get() const { return buffer; }
int* get() { return buffer; }
void increment() const { counter++; }
private:
int* buffer;
mutable int counter;
};
void function(int* n)
{
// These are both okay
C wn(n);
const C wc(n);
// Reading the value is always allowed
printf("%d %d", wn.get()[0], wc.get()[0]);
// Can increment either object's counter
wn.increment();
wc.increment();
// Can set non-const pointer
wn.get()[0] = 5;
// Generates a compiler error
wc.get()[0] = 5;
}
What you can't do with this is neatly arrange for the class to be instantiated with either a int* or a const int*; the two lead to totally different semantics for your class, so you should split it into two if you really need that.
Fortunately, templates make this easy:
template <typename T>
class C
{
public:
C(T* buffer) : buffer(buffer) {}
const T* get() const { return buffer; }
T* get() { return buffer; }
void increment() const { counter++; }
private:
T* buffer;
mutable int counter;
};
Now a C<int> is as above, but a C<const int> only provides read-only access to the buffer, even when the C<const int> object itself is not marked as const:
void function(int* n1, const int* n2)
{
C<int> a(n1);
C<const int> b(n2);
const C<int> c(n1);
const C<const int> d(n2);
// Reading the value is always allowed
printf("%d %d %d %d",
a.get()[0], b.get()[0],
c.get()[0], d.get()[0]
);
// Incrementing the counter is always allowed
a.increment();
b.increment();
c.increment();
d.increment();
// Can set non-const pointer
a.get()[0] = 5;
// Cannot set const pointer, or const/non-const pointer behind const object
//b.get()[0] = 5;
//c.get()[0] = 5;
//d.get()[0] = 5;
}
Live demo
I think that there is a design problem if you want to store two different things which must be handled in different ways in one class. But yes, you can do it:
struct X{};
class A
{
public:
A(const X*) { cout << "const" << endl; }
A(X*) { cout << "non const" << endl; }
};
int main()
{
const X x1;
X x2;
A a1(&x1);
A a2(&x2);
}
the output is expected:
const
non const