I have a class that I use to keep track of the values assumed by a variable. I implemented it by overloading operator=.
Usage example:
myType var0;
var0 = 1;
var0 = 3;
generates on stdout:
1
3
This works fine with variables, but not with arrays. How can I extend this feature?
One way would be overloading the [] operator to return a "proxy" - an object that references your variable, and overloads the = operator to do the tracking.
Here is a sample implementation:
#include <iostream>
using namespace std;
struct myArray;
class proxy {
myArray &array;
int index;
public:
proxy(myArray &_array, int _index)
: array(_array)
, index(_index) {
}
proxy& operator=(int value);
operator int() const;
};
struct myArray {
int data[100];
proxy operator[](int index) {
return proxy(*this, index);
}
};
proxy& proxy::operator=(int value) {
cout << "Asigning " << value << " to element " << index << endl;
array.data[index] = value;
return *this;
}
proxy::operator int() const {
cout << "Reading element at " << index << endl;
array.data[index];
}
int main() {
myArray a;
a[5] = 123;
a[8] = 321;
int x = a[5];
return 0;
}
This prints
Asigning 123 to element 5
Asigning 321 to element 8
Reading element at 5
What you want to do is to use a proxy class for your array and on that class define an operator[] function. Much like how std::vector does it.
You would trace when a non-const reference is produced to an array element. I think that you'd have to assume it was about to be written to. You would make the array out of your existing class so that you'd see the actual write.
The output might look like:
ref to element 32: write 1
Or however you would like it.
Well, arrays don't support operator= anyway, so that is not a real a problem. You can assign to individual array elements of course, but that's already covered by your existing operator=.
You could create a MyArrayType that overloads the [] operator to store the values internally within an array of MyTypes
Related
I wanted a simple class which would encapsulate a pointer and a size, like C++20's std::span will be, I think. I am using C++ (g++ 11.2.1 to be precise)
I want it so I can have some constant, module-level data arrays without having to calculate the size of each one.
However my implementation 'works' only sometimes, dependent on the optimization flags and compiler (I tried on godbolt). Hence, I've made a mistake. How do I do this correctly?
Here is the implementation plus a test program which prints out the number of elements (which is always correct) and the elements (which is usually wrong)
#include <iostream>
#include <algorithm>
using std::cout;
using std::endl;
class CArray {
public:
template <size_t S>
constexpr CArray(const int (&e)[S]) : els(&e[0]), sz(S) {}
const int* begin() const { return els; }
const int* end() const { return els + sz; }
private:
const int* els;
size_t sz;
};
const CArray gbl_arr{{3,2,1}};
int main() {
CArray arr{{1,2,3}};
cout << "Global size: " << std::distance(gbl_arr.begin(), gbl_arr.end()) << endl;
for (auto i : gbl_arr) {
cout << i << endl;
}
cout << "Local size: " << std::distance(arr.begin(), arr.end()) << endl;
for (auto i : arr) {
cout << i << endl;
}
return 0;
}
Sample output:
Global size: 3
32765
0
0
Local size: 3
1
2
3
In this case the 'local' variable is correct, but the 'global' is not, should be 3,2,1.
I think the issue is your initialization is creating a temporary and then you're storing a pointer to that array after it has been destroyed.
const CArray gbl_arr{{3,2,1}};
When invoking the above constructor, the argument passed in is created just for the call itself, but gbl_arr refers to it after its life has ended. Change to this:
int gbl_arrdata[]{3,2,1};
const CArray gbl_arr{gbl_arrdaya};
And it should work, because the array it refers to now has the same lifetime scope as the object that refers to it.
I am implementing a device_vector in Cuda and i am taking ideas from the well known library Thust.
Now for accessing and modifying an element in that device_vector (v), I need to do v[N] = x. For that i need to overload the [] operator.
This is the code used to overload the [] operator :
T& operator[] (unsigned int index)
{
if (index >= numEle)
return ptr[0];
else
return ptr[index];
}
The problem is : To modify any memory location in Device Memory, we need to make a Cuda Kernel Call and a Cuda kernel call cannot return anything.
As far the [] overloading is concerned it returns the reference to the element we want to modify.
How can we do this for a Cuda Kernel ?
Note : I know Thrust Library somehow does this but I am not able to understand how.
The comments have very good pointers, but as an example, you can create an object that will allow you to use the [] operator to write to the CUDA array directly (or do any other things you choose):
struct CudaVector {
unsigned int get(unsigned int index) {
cout << "Get from device: " << index << endl;
return 0; // TODO read actual value
}
void set(unsigned int index, unsigned int value) {
cout << "Set in device: " << index << " " << value << endl;
// TODO write actual value
}
struct Item {
CudaVector& vector;
unsigned int index;
operator unsigned int() const {
return vector.get(index);
}
unsigned int operator=(unsigned int other) {
vector.set(index, other);
return other;
}
unsigned int operator=(const Item& other) {
return (*this = static_cast<unsigned int>(other));
}
};
Item operator[](unsigned int index) {
return Item{*this, index};
}
};
This works like:
CudaVector vector;
unsigned int foo = vector[8];
vector[5] = vector[6] = vector[7];
Output:
Get from device: 8
Get from device: 7
Set in device: 6 0
Set in device: 5 0
Idea is that your operator[] doesn't return a reference, but instead it returns a temporary object that is able to handle 'reads' (using the conversion operator) and 'writes' (using the assignment operator).
(The second overload is there to allow chained assignments, since the first one won't be picked up automatically if you don't assign from unsigned int first.)
How do I change the overloaded operator to return a value instead pf a reference?
#include <iostream>
using namespace std;
class IntList
{
private:
int list[1];
public:
IntList() {list[0] = 0;}
int& operator[] (const int index) {return list[index];}
};
int main()
{
IntList list;
cout << list[0] << endl;
list[0] = 1;
cout << list[0] << endl;
return 0;
}
int operator[] (const int index){}
^^^^^
Just remove the &. Once you do that you cannot use it for assigning values to the array elements.
Difference between returning a reference and a non reference
As you noticed when operator [] returns a reference, it can be used on the left hand side of the assignment. This is possible because when you return by reference the return value of operator [] is an l-value. References are treated as l-values because you can take reference of variables that are stored in memory and have an address.
When operator [] returns by value the expression list[0] = 1; will eventually evaluate[#] to something like,
1=1;
Which is illogical, since 1 is not a l-value, the compiler will generate a diagnostic that the left operand must be a l-value.
[#] Assuming value of the element at subscript 0 is 1:
You can do this by just removing the &, so you have
int operator[] (const int index){}.
However as you noticed then the problem is that you cannot assign to it without a compilation error because the index operator no longer returns an l-value. So I think you should consider why you want to return a value instead of a reference. It is possible that you want a pattern where the index operator cannot be use to assign to the object, maybe for some sort of read only type object. Your other option is to have a separate function to set it because the index operator can no longer be used to do that
In your code example you are using assignment, which requires that you return a reference.
list[0] = 1;
list.operator[](0) = 1;
int& xref = list.operator[](0);
(xref) = 1; // <-- changed the value of list element 0.
Given that you want operator[](int index) to return a value, this would translate to:
int x = list.operator[](0);
x = 1; <-- you changed x, not list[0].
If you want operator[](int index) to return a value but also have list[0] = 1 still work, you're going to need to provide two versions of the operator so that the compiler can determine which behavior you are trying to invoke in a given call:
// const member, returns a value.
int operator[] (const int index) const {return list[index];}
// non const member, which returns a reference to allow n[i] = x;
int& operator[] (const int index) {return list[index];}
Note that they must differ by both return type AND member-constness.
#include <iostream>
using namespace std;
class IntList
{
private:
int list[1];
public:
IntList() {list[0] = 0;}
int operator[] (const int index) const { return list[index]; }
int& operator[] (const int index) {return list[index];}
};
int main(int argc, const char** argv)
{
IntList list;
cout << list[0] << endl;
list[0] = 1;
int x = list[0];
cout << list[0] << ", " << x << endl;
return 0;
}
Working demo: http://ideone.com/9UEJND
I'm implementing an STL set with a complex template parameter type. When inserting in to the set, I want the set to use the less-than operator I've defined for my type. I also want to minimize the quantity of object instantiations of my type. It seems I can't have both.
I've got two minimal examples below, each uses the same C++ class.
#include <iostream>
#include <set>
using namespace std;
class Foo {
public:
Foo(int z);
Foo(const Foo &z);
bool operator<(const Foo &rhs) const;
int a;
};
Foo::Foo(int z)
{
cout << "cons" << endl;
a = z;
}
Foo::Foo(const Foo &z)
{
cout << "copy cons" << endl;
a = z.a;
}
bool
Foo::operator<(const Foo &rhs) const
{
cout << "less than" << endl;
return a < rhs.a;
}
Here's my first main():
int
main(void)
{
set<Foo> s;
s.insert(*new Foo(1));
s.insert(*new Foo(2));
s.insert(*new Foo(1));
cout << "size: " << s.size() << endl;
return 0;
}
That's great because it uses the less-than I've defined for my class, and thus the size of the set is correctly two. But it's bad because every insertion in to the set requires the instantiation of two objects (constructor, copy constructor).
$ ./a.out
cons
copy cons
cons
less than
less than
less than
copy cons
cons
less than
less than
less than
size: 2
Here's my second main():
int
main(void)
{
set<Foo *> s;
s.insert(new Foo(1));
s.insert(new Foo(2));
s.insert(new Foo(1));
cout << "size: " << s.size() << endl;
return 0;
}
That's great because an insertion requires just one object instantiation. But it's bad because it's really a set of pointers, and thus the uniqueness of set members is gone as far as my type is concerned.
$ ./a.out
cons
cons
cons
size: 3
I'm hoping there's some bit of information I'm missing. Is it possible for me to have both minimal object instantiations and appropriate sorting?
You are getting a copy from this: *new Foo(1).
Create this struct:
template<typename T>
struct PtrLess
{
bool operator()(const T *a, const T *b) const
{
return *a < *b;
}
};
Make the map look like set<Foo*, PtrLess<Foo>> s; and then add Foo's like s.insert(new Foo(1));
Note the *
Otherwise, when the map creates a container for the Foo item, since it is allocated within the foo containers definition, the map has to copy the supplied value into its internal Foo object.
Standard containers store a copy of the items that are added. If you want your set to store objects, rather than pointers you should simply do the following, otherwise you're creating a memory leak, since the objects allocated via new are never free'd via a corresponding delete.
int main()
{
set<Foo> s;
s.insert(Foo(1));
s.insert(Foo(2));
s.insert(Foo(1));
cout << "size: " << s.size() << endl;
return 0;
}
If you want to minimise the number of temporary objects instantiated, just use a single temporary:
int main()
{
set<Foo> s;
Foo temp(1);
s.insert(temp);
temp.a = 2;
s.insert(temp);
temp.a = 1;
s.insert(temp);
cout << "size: " << s.size() << endl;
return 0;
}
The output for this snippet (via ideone) is:
cons
copy cons
less than
less than
less than
copy cons
less than
less than
less than
size: 2
Generally, I would prefer to store the actual objects in a set<Foo> rather than pointers to objects in a set<Foo*>, since there can be no problems with object ownership (who/when new and delete need to be called), the total amount of memory allocated is smaller (for N items you need N*sizeof(Foo) rather than N*(sizeof(Foo) + sizeof(Foo*)) bytes) and data access could typically be expected to be faster (since there's no extra pointer indirection).
Hope this helps.
This is an extension to #Mranz's answer. Instead of dealing with raw pointers, put the pointers in an std::unique_ptr
#include <memory>
using namespace std;
template<typename T>
struct PtrLess
{
bool operator()(const T& a, const T& b) const
{
return *a < *b;
}
};
int
main(void)
{
set<unique_ptr<Foo>, PtrLess<unique_ptr<Foo>>> s;
s.insert(unique_ptr<Foo>(new Foo(1)));
s.insert(unique_ptr<Foo>(new Foo(2)));
s.insert(unique_ptr<Foo>(new Foo(1)));
cout << "size: " << s.size() << endl;
return 0;
}
Consider following class
class test
{
public:
test(int x){ cout<< "test \n"; }
};
Now I want to create array of 50 objects of class test . I cannot change class test.
Objects can be created on heap or stack.
Creating objs on stack is not possible in this case since we dont have default constructor in class
test objs(1)[50]; /// Error...
Now we may think of creating objs on heap like this..
test ** objs = NULL;
objs = (test **) malloc( 50 * sizeof (test *));
for (int i =0; i<50 ; ++ i)
{
objs[i] = new test(1);
}
I dont want to use malloc .Is there any other way??
If you guys can think of some more solutions , please post them...
You cannot create an array of objects, as in Foo foo [N], without a default constructor. It's part of the language spec.
Either do:
test * objs [50];
for() objs[i] = new test(1).
You don't need malloc(). You can just declare an array of pointers.
c++decl> explain int * objs [50]
declare objs as array 50 of pointer to int
But you probably ought to have some sort of automatic RAII-type destruction attached.
OR subclass test publicly:
class TempTest : public test
{
public:
TempTest() : test(1) {}
TempTest(int x) : test(x) {}
TempTest(const test & theTest ) : test(theTest) {}
TempTest(const TempTest & theTest ) : test(theTest) {}
test & operator=( const test & theTest ) { return test::operator=(theTest); }
test & operator=( const TempTest & theTest ) { return test::operator=(theTest); }
virtual ~TempTest() {}
};
and then:
TempTest array[50];
You can treat every TempTest object as a test object.
Note: operator=() & copy constructor are not inherited, so respecify as necessary.
Why do you need array?
std::vector<test*> v(50);
Or as #j_random_hacker suggested in the comments:
std::vector<test> v(50, test(1));
An example:
/** g++ -Wall -o vector_test *.cpp && vector_test */
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
struct Test {
int value;
Test(int x) : value(x)
{
std::cout << "Test(" << value << ")" << " ";
}
operator int() const
{
std::cout << "int(" << value << ")" << " ";
return value;
}
};
int main()
{
using namespace std;
vector<Test> v(5, Test(1));
cout << endl;
copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
cout << endl;
v[1] = 2;
v[2].value = 3;
cout << endl;
copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
cout << endl;
return 0;
}
Output:
Test(1)
int(1) 1 int(1) 1 int(1) 1 int(1) 1 int(1) 1
Test(2)
int(1) 1 int(2) 2 int(3) 3 int(1) 1 int(1) 1
Contrary to what many people believe, you can actually create an array of objects that do not have a default constructor. What you cannot do is make it use a set of arguments for all constructor invokations. You just have to initialize all elements of it. That is, you can do the following:
#define PRINTT(z, n, initializer) initializer
test objs[50] = {
BOOST_PP_ENUM(50, PRINTT, 1) // yields 1, 1, 1, .... 1
};
#undef PRINTT
That will initialize all 50 elements with 1. boost::pp is used to print a 1 50 times in a row automatically.
I think that other responders are treating this question too literally.
If all you really want to do is make a "group" of 50 objects that you can treat as an array, then by far the easiest and most maintainable way of accomplishing what you're trying to do is:
std::vector<test> objs(50, test(1));
This declares a vector of 50 objects, each of which is a copy of test(1). A vector is basically a C++ growable array; although you may not need the growability, the fact that it can be called with a 2-arg constructor that copy-constructs each element is useful here.
You can use this more-or-less exactly like an array -- e.g. the 5th element is objs[4]. Performance is the same too -- the C++ standard guarantees that internally the elements are stored in a contiguous array.
You don't need malloc(). You can use new for the array of pointers, too:
test **objs = new test* [50];
Boost's Pointer Container library might come to rescue here. With boost::ptr_vector<T> you can hold a list of heap-allocated objects which can be even polymorphic (virtual functions), which isn't possible with simply std::vector<T>.
Unlike std::vector<T>, the objects won't be stored in subsequential memory addresses. Things like resizing the container however will be faster because the elements will keep their original memory addresses. The best bonus is, you don't need to call delete yourself: the contained objects will be destroyed when the ptr_vector goes out of scope. Example:
#include <boost/ptr_vector.hpp>
#include <iostream>
class test() {
protected:
int const i;
public:
explicit test(int i) : i(i) {}
virtual void who_am_i() const { std::cout << "I am test " << i << std::endl; }
};
class special_test : public test {
public:
explicit special_test(int i) : test(i) {}
virtual void who_am_i() const { std::cout << "I am special_test " << i << std::endl; }
};
int main() {
boost::ptr_vector<test> objs;
for (int i=0; i<50; ++i)
objs.push_back(new test(i)); // NB: constructing to heap here!
objs.push_back(new special_test(123)); // objs can also hold inherited classes
objs[13].who_am_i(); // outputs: I am test 13
objs[50].who_am_i(); // outputs: I am special_test 123
} // all created objects are automatically destroyed here