GNU vs Visual C++ compiler : Different behaviour

Apr 17, 2009 at 10:12am
All,

I'm taking up C++ again after 10 years of Java :-)
As an exercise (to get rid of some of the rustyness) I developed a class template (and a specialization) that is about creating multi-dimensional dynamic (as in run-time dimensioned) arrays. (code below)

The specialized template serves to end the recursion over multiple dimensions (as a matter of fact it's really the class template itself that ends te recursion)

The trouble is that when I instantiate such an Array object ...
1
2
3
4
5
vector<unsigned> args;
args.push_back(4);
args.push_back(3);
args.push_back(2);
Array<Array<Array<float,unsigned>,unsigned>,unsigned> *cube=new Array<Array<Array<float,unsigned>,unsigned>,unsigned>(args);

... the gnu compliler throws an error at me stating that ...
error: no matching function for call to `Array<Array<float, unsigned int>, unsigned int>::operator new [](unsigned int, std::vector<unsigned int, std::allocator<unsigned int> >)'
note: candidates are: static void* Array<Array<T, I>, I>::operator new [](size_t, std::vector<I, std::allocator<_T2> >&) [with T = float, I = unsigned int]

The error occurs on line 12 of the specialized template implementation codeblock below. The candidate can be found on line 22 of the same codeblock.

I fail to understand what I do wrong and neither does the MS Visual C++ compiler since it's perfectly happy and compiles just fine.

Any ideas anyone?

Thanks in advance,

Peter.

template declaration
1
2
3
4
5
6
7
8
9
10
11
12
13
template<class T,class I>
class Array
{
private:
	I arLength;
	T *ar;
public:
	Array();
	Array(I length);
	~Array();
	void * operator new[](size_t size,vector<I> &dimensions);
	T &operator[](I index);
};


specialized template decaration
1
2
3
4
5
6
7
8
9
10
11
12
13
template<class T,class I>
class Array<Array<T,I>,I>
{
private:
	I arLength;
	Array<T,I> *ar;
public:
	Array();
	Array(vector<I> &dimensions);
	~Array();
	void * operator new[](size_t size,vector<I> &dimensions);
	Array<T,I> &operator[](I index);
};


template implementation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
template<class T,class I>
Array<T,I>::Array()
{
	//default constructor required for array instantiation
}

template<class T,class I>
Array<T,I>::Array(I length)
:
arLength(length),
ar(new T[arLength])
{
}

template<class T,class I>
Array<T,I>::~Array()
{
	delete[] ar;
}

template<class T,class I>
void *Array<T,I>::operator new[](size_t size,vector<I> &dimensions)
{
	char *bufPtr=new char[size];
	char *elPtr=bufPtr+4;
	for(unsigned idx=0;idx<(size / sizeof(Array<I,I>));idx++,elPtr+=sizeof(Array<I,I>))
	{
		new(elPtr)Array<T,I>(*dimensions.begin());
	}
	return bufPtr;
}

template<class T,class I>
T &Array<T,I>::operator[](I index)
{
	if(index<0 || index>=arLength)
	{
		cerr<<"index out of bounds : "<<index<<":"<<arLength<<endl;
		exit(1);
	}
	return ar[index];
}


specialized template implementation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
template<class T,class I>
Array<Array<T,I>,I>::Array()
{
	//default constructor required for array instantiation
}

template<class T,class I>
Array<Array<T,I>,I>::Array(vector<I> &dimensions)
:
arLength(*dimensions.begin()),
ar(new(vector<I>(dimensions.begin()+1,dimensions.end())) Array<T,I>[arLength])
{
}

template<class T,class I>
Array<Array<T,I>,I>::~Array()
{
	delete[] ar;
}

template<class T,class I>
void *Array<Array<T,I>,I>::operator new[](size_t size,vector<I> &dimensions)
{
	char *bufPtr=new char[size];
	char *elPtr=bufPtr+4;
	for(unsigned idx=0;idx<(size / sizeof(Array<Array<T,I>,I>));idx++,elPtr+=sizeof(Array<Array<T,I>,I>))
	{
		new(elPtr)Array<Array<T,I>,I>(dimensions);
	}
	return bufPtr;
}

template<class T,class I>
Array<T,I> &Array<Array<T,I>,I>::operator[](I index)
{
	if(index<0 || index>=arLength)
	{
		cerr<<"index out of bounds : "<<index<<":"<<arLength<<endl;
		exit(1);
	}
	return ar[index];
}
Apr 17, 2009 at 10:28am
I can't see why you're overloading operator new. Memory aquistion and initialisation should be performed in the constructor. You only tend to overload new/delete if there a special placement considerations, like a custom heap.

I'd expect the constructor to take a vector<T> rather than a vector<I>. Perhaps I'm wrong.
Apr 17, 2009 at 10:50am
well if i remember correctly instantiating arrays of objects implies invoking the default constructor of the element class.
Overloading operator new[] provides the possibility to initiate the elements differently.
This is how i initiate my multi dim arrays recursively.
Apr 17, 2009 at 11:06am
No it doesn't, that view is incorrect. Overloading new/delete is only concerned with the placement of the objects, not the content of the objects content themselves. Initialisation is performed by the constructor.
Apr 18, 2009 at 8:32am
my question was really about GNU vs Visual C++ compiler but anyhow, I never back away from an intresting discussion.
My C++ may be rusty but I'm not braindead yet, so it's coming back to me reasonably fast.

First of all, just to make sure, I'm overloading operator new[](...) and not operator new(...) which would indead not make sense for my use case.

secondly, remember this thing compiles fine with the Visual C++ compiler and behaves as expected at runtime i.e. semantics are as expected and no apparent memory leaks.

Now, concerning the behaviour of operator new[](...). I don't know what it was originally meant for but I'm taking advantage of the features it offers.

This is how it works:

It requires an size_t size argument and optionally some extra arguments of your choice and you're expected to hand back a void * pointer to some block of memory.

It is invoked using something like

new(extraArgsIfAny) SomeClass[length]

not to be confused with the "placement new" operator

new(void *memory) SomeClass(arguments)

In the "placement new" variant of operator new the operator accepts a block of memory and initializes it using a constructor of choice. The block of memory should of course be large enough to contain the object, but apart from that no other requirements exist. I'm using this also in the body of my operator new[] implementation.

The size_t argument handed over to the method equals 4+sizeof(SomeClass)*length. So you need to hand back a void * pointer to a block of memory of that size.

Upon exit of this method, the runtime will fill up this memory block with:
* an unsigned int (the first four bytes) containing the length (# of elements) of the array.
* # of elements instances of SomeClass using the default constructor

What I'm doing in the body of my operator new[] implementation is the following:

1
2
3
4
5
6
7
8
char *bufPtr=new char[size];
char *elPtr=bufPtr+4; //leave room for the unsigned int containing the length of the array
for(unsigned idx=0;idx<(size / sizeof(SomeClass));idx++,elPtr+=sizeof(SomeClass))
{
	new(elPtr)SomeClass(...); // this is the "placement new" mentioned above using any constructor of SomeClass you want.
	//possibly mutate the array element some more.
}
return bufPtr;


So, I'm filling up the buffer myself like I want it. Upon exit the runtime will try to fill it again using the default constructor SomeClass but if you see to it that it does not initiate any members, you're fine and the array will be filled the way you want it to contain.

Note that when you create an instance like SomeClass *ar=new(extraArgsIfAny) SomeClass[length] that ar itself will point to bufPtr+4.

Also note that I'm doing this in a recursive fashion like this:

1
2
3
4
5
6
7
8
9
10
void *Array<T,I>::operator new[](size_t size,vector<I> &dimensions)
{
	char *bufPtr=new char[size];
	char *elPtr=bufPtr+4;
	for(unsigned idx=0;idx<(size / sizeof(Array<T,I>));idx++,elPtr+=sizeof(Array<T,I>))
	{
		new(elPtr)Array<T,I>(*dimensions.begin());
	}
	return bufPtr;
}


hence the need for a specialized template to end the recursion ... but that's another story.

PS. call me a purist but isn't this more a kind of overriding rather than overloading ;-)
Apr 18, 2009 at 9:23am
Oh and of course I do realize that there are other ways of solving this array initialization problem (although I can think of other use cases where this would be the preferred course of action).

Remember however that this was just one of the exercises I devised for myself in order to get re-acquainted with C++ again
Apr 19, 2009 at 11:31am
My apologies. I've taken another look at your code and withdrawn my comments.
Topic archived. No new replies allowed.