Making it easier to refer to a member structure

Hey forum!

I am working on my own templatized dynamic array class. I understand a similar class is already provided in the HP standard template library--or at least it used to be. However, I thought it would be a useful learning experience to write one from scratch. I hoped doing so might help me become more confident with programming using templates and also as a general 'warm up' to ease me back in to C++ as a whole.

In order to make copy-construction and copy-assignment as efficient as possible I keep the actual array data itself within a dynamically allocated reference-counted structure. I then pass pointers to this and increment the reference count as necessary. As an aside; I am starting to suspect the new 'move' semantics introduced in the fantastic C++11 upgrade to the language make copy-on-change and reference counting an obsolete approach. Still that is a matter for its own question.

To prevent this structure being abused by any future end-users (although obviously in my case no one else will ever use this!) I declare it as a private member of my dynamic array class.

So, my declaration looks like this with most of the irrelevant parts elided:

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
template<typename typeArray>
class TCDynamicArray {
public://Constructors and destructor.
	TCDynamicArray();
        ...//Constructors that allow you to predefine the array size on declaration.
	TCDynamicArray(const TCDynamicArray<typeArray> &tcArray);
	TCDynamicArray(TCDynamicArray<typeArray> &&tcArray);
        ...//Conversion constructors that allow
           //the array to copy its data from other statically allocated array types.
	~TCDynamicArray();
public://Operators for 'copy' and 'move' assignment.
	const TCDynamicArray<typeArray>& operator=(const TCDynamicArray<typeArray> &TcArray);
	const TCDynamicArray<typeArray>& operator=(TCDynamicArray<typeArray> &&TcArray);
public://Public member functions.
       ...//Functions which allow a new element to be 
          //pushed onto/popped off the start or end of the array
private://Private structures.
	template <typename typeArray>
	struct TsARRAYCORE {
		int m_iArrayLength;
		int m_iIndexMaximum;
		int m_iIndexNext;
		int m_iReferenceCount;
		typeArray *m_pTArray;
	};
private://Private member functions.
	...//Core allocation and deallocation functions primarily.
private://Private data members.
	TCDynamicArray<typeArray>::TsARRAYCORE<typeArray> *m_pTsCore;
};


This works so far, so well in my testing at least. However every time I need to allocate a new instance of the array core structure I have to type in the very long TCDynamicArray<typeArray>::TsARRAYCORE<typeArray> declaration. This is both tedious and continually invutes typing mistakes...

I wonder if there is some dodge I can use to make this declaration simpler and less error-prone?

I considered trying typedef sARRAYCORE TCDynamicArray<typeArray>::TsARRAYCORE<typeArray>, but this gave me errors which I think were related to scope. I suppose a pre-processor macro would work, but that short circuits all the handy type-checking capabilities of VisualStudio intellisense and the compiler itself.

I would welcome any suggestions or comments.
Last edited on
it looks like your typedef is backwards.
try
typedef TCDynamicArray<typeArray>::TsARRAYCORE<typeArray> sARRAYCORE

a #define might be ugly but you could parameterize it to use any type you wanted in a shorter syntax.




Last edited on
I understand a similar class is already provided in the HP standard template library

Welcome, time traveler. HP cut funding to the library you mention all the way back in 1994 (at which point SGI temporarily picked it up), when it became clear that C++ will in fact provide an equivalent, ultimately came to be known as std::vector.

Also, what compiler are you using? With the triple-dots commented out, I get
gcc:

prog.cc:18:12: error: declaration of template parameter 'typeArray' shadows template parameter
  template <typename typeArray>
            ^~~~~~~~
prog.cc:1:10: note: template parameter 'typeArray' declared here
 template<typename typeArray>
          ^~~~~~~~
1

clang:
prog.cc:18:21: error: declaration of 'typeArray' shadows template parameter
        template <typename typeArray>
                           ^
prog.cc:1:19: note: template parameter is declared here
template<typename typeArray>
                  ^


Fixing that and trimming redundant words, your template can be defined as:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template<typename typeArray>
class TCDynamicArray {
public:
	TCDynamicArray();
	TCDynamicArray(const TCDynamicArray &tcArray);
	TCDynamicArray(TCDynamicArray &&tcArray);
	~TCDynamicArray();
public:
	const TCDynamicArray& operator=(const TCDynamicArray &TcArray);
	const TCDynamicArray& operator=(TCDynamicArray &&TcArray);
private:
	struct TsARRAYCORE {
		int m_iArrayLength;
		int m_iIndexMaximum;
		int m_iIndexNext;
		int m_iReferenceCount;
		typeArray *m_pTArray;
	};
private:
	TsARRAYCORE *m_pTsCore;
};
Many thanks jonnin!!!

And right you are--that typedef is back to front... Groan.

Is using a #define macro an acceptable approach to this situation then? It certainly makes sense, I was just unsure it might be a bit much of a kludge.

Hey cubbi!!!

Yeah--I am thoroughly superannuated... I came to C++ from C in 1986, but for various reasons packed it in back in the mid-nineties when having a full copy of VC++ v4.2 Enterprise and its support of a dedicated 'bool' type were both something to crow over!!! The fact I used to enjoy programming so much and that you can now get Visual Studio for free instead of £500+ twisted my arm to get back in to it a little. I am a pure hobbyist though and always have been. On that score I'm a bit sorry Turbo C++ seems to have vanished over the intervening years, along with Borland's cool 'new' object pascal derived 'Delphi' language. That also used to be a huge deal in the programming advice columns of 'Personal Computer World'!!!

I am using the version of C++ that comes with VS2017 community. Aside from chopping the unnecessary bits out that sample was copied and pasted directly from the text editor. Maybe I cocked something up in the process though!
Last edited on
I am using the version of C++ that comes with VS2017 community

I see, it indeed permits this nested template, as well as Intel C++, while gcc and clang refuse to compile it.

The reason this is an error is that the two typeArrays in your original declaration are unrelated. Given

1
2
3
4
5
6
7
8
template<typename typeArray>
class TCDynamicArray {
...
	template <typename typeArray>
	struct TsARRAYCORE {
                typeArray *m_pTArray; // not the TCDynamicArray's typeArray
        };
};


you may have TCDynamicArray<int>::TsARRAYCORE<double>
or TCDynamicArray<string>::TsARRAYCORE<bool>
etc.

standard C++ wants you to name those two unrelated typeArray template parameters differently, to avoid confusion.

From the description, it sounds as though the intent is that for TCDynamicArray<int>, you want m_pTArray to be of type int*. If that is the case, the nested class should not be a template on its own:
1
2
3
4
5
6
7
template<typename typeArray>
class TCDynamicArray {
...
	struct TsARRAYCORE {
                typeArray *m_pTArray; // TCDynamicArray's typeArray
        };
};

Last edited on
That makes a lot of sense!!! Many thanks cubbi--I do follow what you mean.

I could never understand how to use templates back in the past so this is exactly the type of thing I want to get thoroughly nailed down in my mind!

Lots of subject-specific terms like 'iterators' and 'vectors' do still go over me head though. However I really like the new for( : ) which works like foreach in perl. Very cool and handy and I think that works through iterators. I don't know if they still talk about 'sequencers' for arrays and linked-lists but the for( : ) language feature seems to replace that approach entirely and it was very easy to get it working for this dynamic array class I have described above.
Last edited on
On that score I'm a bit sorry Turbo C++ seems to have vanished over the intervening years

Many compilers came and went. I think compiler support page on cppreference, http://en.cppreference.com/w/cpp/compiler_support gives a good idea of which C++ compilers are active and kicking and which are alive and fading out.
Actually C++ Builder and Delphi are back for free. See link in the Lounge
Cool, I always liked builder. Turbo was one step up from notepad coding and while I used it (used turbo pascal more) it was never a loved tool so much as "this is what I have".
I primarily used the DOS version of Turbo C++, alongside TASM for a while--although I really struggled with writing anything in assembler no matter what application I used. Under Windows I do not think I ever ran anything but Visual C++, even for Window 3.1. However many people swore by the Watcom C++ compiler in that environment because of the speed of its executables and the variety of platforms it would produce binaries for. Sadly I notice Watcom seems to have gone the same way as Borland and vannished without a trace! I recall the real buzz about Delphi was how it combined a language as 'easy to use' as Visual Basic, but produced native binaries that ran as quickly as C/C++. However I also seem to remember there was a version of Visual BASIC that actually produced true standalone executables instead of pseudo code, so I dare say that stole some of Delphi's thunder. That was two or three years after I gave up programming though and it has probably all changed with this new '.NET' and CLI 'managed' stuff.

An update in regards the rather confusing templated, member struct situation as mentioned above. cubbi pointed out the problem with how I originally had it written. However, I found if I removed the template <typename typeArray> from the struct declaration as advised then I received the following message from the VC++ compiler, followed by a cascade of resultant errors:

warning C4346: 'TsARRAYCORE': dependent name is not a type

It took some trial and error, but it turned out the final answer was to use the following form every time I declared an instance of the struct in question, including when indicating a function return type (in pointer form):

typename TCDynamicArray<typeArray>::TsARRAYCORE

I have no idea what the difference is, but it seems to work!
Last edited on
> typename TCDynamicArray<typeArray>::TsARRAYCORE
> I have no idea what the difference is, but it seems to work!

See: http://www.cplusplus.com/forum/general/219039/
If you're still inside the TCDynamicArray template, you don't need all that "TCDynamicArray<typeArray>::" stuff either. It's just TsARRAYCORE *m_pTsCore; - the declaration I posted in my first response (after "trimming redundant words") compiles in visual studio (and gcc/clang/intel)
Last edited on
macros are usually a hack, but it works here for a simple way to 1-line fix what you have.

use the typedef if you only need 1 template type. The macro would let you have one instance that is a <double> and another that is <int> or whatever. If you don't need that, the typedef is safer and cleaner.

Last edited on
This discussion has helped me a great deal, particularly in regards where certain names are valid within a piece of code. Many thanks guys!!!

There is still one point I am a little unclear on.

As cubbi says, inside the class/template then I can just use TsARRAYCORE without any scope specification. So, for example inside a member function or as a data member declaration:

TsARRAYCORE *pTsNewCore{ nullptr }

is fine.

This is also the case for member function arguments, so:

void AllocateCore(TsARRAYCORE *pTsOldCore)

also works.

However if I try to have a member function return a value of this type like:

TsARRAYCORE* AllocateCore(TsARRAYCORE *pTsOldCore)

then I get an error.

In this situation I must use:

typename TCDynamicArray<typeArray>::TsARRAYCORE* AllocateCore(TsARRAYCORE *pTsOldCore)

Can anyone explain why this is the case for return values?
Last edited on
I can't actually reproduce that issue: defining a "TsARRAYCORE* AllocateCore(TsARRAYCORE *pTsOldCore)" after the definition of TsARRAYCORE compiles and runs in the four compilers I have available. Do you have a minimal example that reproduces the problem?
Last edited on
I think this is what Lucian Valois is referring to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template < typename T > struct TCDynamicArray {

	struct TsARRAYCORE { /* ... */ } ;
    
    // unqualified names: typename disambiguator is not required here
	TsARRAYCORE* AllocateCore( TsARRAYCORE* /* pTsOldCore */ ) { return nullptr ; }

    // unqualified names: typename disambiguator is not required here
	TsARRAYCORE* AllocateCore_2( TsARRAYCORE* /* pTsOldCore */ ) ;
};

template < typename T > 
typename TCDynamicArray<T>::TsARRAYCORE* // qualified name: typename disambiguator is required here
TCDynamicArray<T>::AllocateCore_2( TsARRAYCORE* /* pTsOldCore */ ) // unqualified name: typename disambiguator is not required here
{ return nullptr ; }
Yep--that is exactly the situation I meant JLBorges!!! Right down to it working properly if you write the function as inline within the class declaration, but not if you use a separate definition.

I have read that post you linked to and am trying to digest it. I think it will take me a few moments to actually understand it though!!! Templates really introduce a lot of complexity. I think this is why I did not use them before.

One quick aside, I have noticed many people use a single capital letter for the name of the variable type in a template declaration, like template <typename T>. Is this just a convention or is it something important to the mechanics that I have missed?
Last edited on
> I have noticed many people use a single capital letter for the name of the variable type in a
> template declaration, like <template typename T>. Is this just a convention?

It is just a convention. In general, T denotes any type.

We can use semantically richer names if it aids readability.
For example: template < typename NUMERIC_TYPE > struct matrix
Topic archived. No new replies allowed.