tags:

views:

239

answers:

4

Can somebody explain to me why the following works:

template<class T> class MyTemplateClass {
public:
    T * ptr;
};

int main(int argc, char** argv) {
    MyTemplateClass<double[5]> a;
    a.ptr = new double[10][5];
    a.ptr[2][3] = 7;
    printf("%g\n", a.ptr[2][3]);
    return 0;
}

But this doesn't:

class MyClass {
public:
    double[5] * ptr;
    // double(*ptr)[5]; // This would work
};

int main(int argc, char** argv) {
    MyClass a;
    a.ptr = new double[10][5];
    a.ptr[2][3] = 7;
    printf("%g\n", a.ptr[2][3]);
    return 0;
}

Obviously there is more to template instantiation than just a textual replacement by the arguments to the template - is there a simple explanation of this magic?

For the latter the compiler (g++ 4.1.2) spits out the following error:

test.cxx:13: error: expected unqualified-id before '[' token

Where line 13 is the double[5] * ptr; line.

The question is not:

"Why does the MyClass example fail? - because C++ doesn't allow Java style array declarations ;-)".

But is:

"Why does the MyTemplateClass example succeed?"

+3  A: 
template<class T> MyTemplateClass {
    ...
}

is closer to

template<class T> MyTemplateClass {
    typedef {actual type} T;
    ...
}

than to a simple text substitution.

erikkallen
A: 

There is a difference between using a pointer or array of pointers. There is a need to allocate memory for each element of your array before you're going to use it, and yes you have a typo in second example you didn't defined template.

Artem Barger
+8  A: 

The difference lies in the C++ grammar. A simple-declaration is formed like this:

declaration-specifier-seq init-declarator-list

Where declaration-specifier-seq is a sequence of declaration specifiers:

simple-type-specifier: int, bool, unsigned, typedef-name, class-name ...
class-specifiers: class X { ... }
type-qualifier: const, volatile
function-specifier: inline, virtual, ... 
storage-class-specifier: extern, static, ...
typedef

You get the idea. And init-declarator-list is a list of declarators with an optional initializer for each:

a
*a
a[N]
a()
&a = someObj

So a full simple-declaration could look like this, containing 3 declarators:

int a, &b = a, c[3] = { 1, 2, 3 };

Class members have special rules to account for the different context in which they appear, but they are very similar. Now, you can do

typedef int A[3];
A *a;

Since the first uses the typedef specifier and then simple-type-specifier and then a declarator like "a[N]". The second declaration then uses the typedef-name "A" (simple-type-specifier) and then a declarator like "*a". However, you of course cannot do

int[3] * a;

Since "int[3]" is not a valid declaration-specifier-seq as shown above.

And now, of course, a template is not just like a macro text substitution. A template type parameter of course is treated like any other type-name which is interpreted as just the type it names and can appear where a simple-type-specifier can appear. Some C# folks tend to say C++ templates are "just like macros", but of course they are not :)

Johannes Schaub - litb
A: 

The line

MyTemplateClass<double[5]> a;

is, from what I can tell, semantically equivalent to

MyTemplateClass<double*> a;

Thus, all of your assignments to a.ptr work fine. As litb points out, your second attempt violates the declaration syntax and fails.

Harper Shelby
Not at all. In the former, the "ptr" variable will be a "pointer to an array" i.e. double (*ptr)[5]. In the latter, the "ptr" variable will be a "pointer to a pointer" i.e. double **ptr. They are completely different types. The latter won't work for "a.ptr = new double[10][5]", because, for example, there are no pointers in that structure (it's just a 2-dimensional array).
newacct