Strange bug: struct is not a type in C++?

J

jdmuys

Hi,

I have a strange bug in my code, which I managed to reduce to the tiny
C++ program below.

The compiler reject the "class1<Type>::insideStruct *p2;" declaration
with the following error message:

/.../main.cpp:23: error: expected ';' before '*' token

while prepending "struct" in front of the declaration makes it quite
happy.

Since in C++, structs are types, this shouldn't be: either they are
both correct or they are both incorrect.

Did I miss something? Is this a compiler bug (I tried two different
compilers including GCC4 on MacOS X)? or my bug?

Thanks for the help,

Jean-Denis.

#include <iostream>

template<class Type>
class class1
{

public:
struct insideStruct
{
int anyInt;
Type fData;
};

insideStruct class1Data;
};

template<class Type>
class class2
{
public:
int class2int;
struct class1<Type>::insideStruct *p1; // Compiler is just fine with
this
class1<Type>::insideStruct *p2; // but doesn't like this
};


int main (int argc, char * const argv[]) {
std::cout << "Hello, Lucky World!\n";
class1<double> c1;
class2<double> c2;
std::cout << "class1 size: " << sizeof(c1) << "\n";
std::cout << "class2 size: " << sizeof(c2) << "\n";
return 0;
}
 
M

Marcel Müller

Hi,
template<class Type>
class class2
{
public:
int class2int;
struct class1<Type>::insideStruct *p1; // Compiler is just fine with
this
class1<Type>::insideStruct *p2; // but doesn't like this

typename class said:

Implicit typenames in templates are deprecated for a while.


Marcel
 
L

Leandro Melo

Hi,

I have a strange bug in my code, which I managed to reduce to the tiny
C++ program below.

The compiler reject the "class1<Type>::insideStruct *p2;" declaration
with the following error message:

/.../main.cpp:23: error: expected ';' before '*' token

while prepending "struct" in front of the declaration makes it quite
happy.

Since in C++, structs are types, this shouldn't be: either they are
both correct or they are both incorrect.

Did I miss something? Is this a compiler bug (I tried two different
compilers including GCC4 on MacOS X)? or my bug?

Thanks for the help,

Jean-Denis.

#include <iostream>

template<class Type>
class class1
{

public:
        struct insideStruct
        {
                int anyInt;
                Type fData;
        };

        insideStruct class1Data;

};

template<class Type>
class class2
{
public:
        int class2int;
        struct class1<Type>::insideStruct *p1; // Compiler is just fine with
this
        class1<Type>::insideStruct *p2; // but doesn't like this

};

int main (int argc, char * const argv[]) {
   std::cout << "Hello, Lucky World!\n";
        class1<double> c1;
        class2<double> c2;
        std::cout << "class1 size: " << sizeof(c1) << "\n";
        std::cout << "class2 size: " << sizeof(c2) << "\n";
   return 0;

}

Try this:

typename class1<Type>::insideStruct *p2;
 
J

jdmuys

typename class1<Type>::insideStruct *p2;

Yep, that's it: the line must be prepended by the "typename" keyword.

The reason is that there is no unambiguous way to tell what the
"class1<Type>::X *p2" is until class1 is instanciated. It can be two
very different things:

1- A pointer declaration, when X is a type inside class1. This is the
original intent.
2- a multiplication (!!), when X is (for example), an int member of
class1. This could happen.

This is why the C++ standard now says that an identifier that is both
qualified (using the :: scope resolution operator) AND dependent
(parameterized) is NOT automatically a type.

Prepending it with "typename" makes it a type:

typename class1<Type>::insideStruct *p2;

This also explains why prepending struct works as well. With struct,
the line says that X is a struct, and since a struct is automatically
a type in C++, the compiler is happy.

However the "struct" workaround is less general and not idiomatic.

I had not written any significant C++ code since 2003. Since then the C
++ standard has evolved!

Thanks and best regards to all.

Jean-Denis
 
J

Juha Nieminen

jdmuys said:
The reason is that there is no unambiguous way to tell what the
"class1<Type>::X *p2" is until class1 is instanciated. It can be two
very different things:

1- A pointer declaration, when X is a type inside class1. This is the
original intent.
2- a multiplication (!!), when X is (for example), an int member of
class1. This could happen.

When you want to specify option 1, you use the keyword 'typename'.
However, how can you specify that you really want it to be interpreted
as option 2?

(Or is it so that it's by default interpreted as option 2, which
causes the error because the parameters are not valid for the binary
operator*, and you have to specify the 'typename' if you want to tell
the compiler that this is a type declaration, not a call to operator*?)
 
J

James Kanze

When you want to specify option 1, you use the keyword
'typename'. However, how can you specify that you really want
it to be interpreted as option 2?
(Or is it so that it's by default interpreted as option 2,
which causes the error because the parameters are not valid
for the binary operator*, and you have to specify the
'typename' if you want to tell the compiler that this is a
type declaration, not a call to operator*?)

The goal is that the compiler can reasonably parse a template
before any instantiation. To do this, practically, for each
symbol, it must know whether the symbol is bound to a type, a
template or something else. For dependent symbols (those whose
actual binding depends on the instantiation), the compiler
assumes something else, unless you tell it differently (typename
or template keywords). If, during actual instantiation, it
turns out that the assumption was wrong (you lied, or you forgot
to tell it), the program is ill-formed, and you get an error
message.
 
J

jdmuys

  When you want to specify option 1, you use the keyword 'typename'.
However, how can you specify that you really want it to be interpreted
as option 2?

  (Or is it so that it's by default interpreted as option 2, which
causes the error because the parameters are not valid for the binary
operator*, and you have to specify the 'typename' if you want to tell
the compiler that this is a type declaration, not a call to operator*?)

In essence yes: the compiler (GCC4 at least) defaults to
multiplication, as the example below shows. Look in class2method():

std::cout << class1<Type>::value *p4 << "\n"; // multiplication case
std::cout << class1<Type>::insideStruct *p3; // there is no way to
use the typename case with cout

the first line above compiles fine and prints 15 as expected.
the second line above doesn't compile with the following error
message:

/.../main.cpp:38: error: dependent-name 'class1<Type>::insideStruct'
is parsed as a non-type, but instantiation yields a type

It says the line is parsed as "non-type": the defaults is thus
multiplication.

The full program code follows: simply comment out that failing line,
and all runs fine then.

Jean-Denis


#include <iostream>

template<class Type>
class class1
{

public:
struct insideStruct
{
int anyInt;
Type fData;
};

insideStruct class1Data;
const static int value = 3;
};

template<class Type>
class class2
{
public:
int class2int;
struct class1<Type>::insideStruct *p1; // Compiler is just fine with
this
typename class1<Type>::insideStruct *p2; // and now also with this

void class2method()
{
std::cout << "trying both use case inside a parameterized class
member function\n";
std::cout << p1 << "\n";
std::cout << p2 << "\n";

typename class1<Type>::insideStruct *p3; // type case
std::cout << p3 << "\n";
int p4 = 5;
class1<Type>::value *p4; // multiplication case
std::cout << class1<Type>::value *p4 << "\n"; // the same used with
cout
// note: there is no way to use the typename case with cout, such
as:
std::cout << class1<Type>::insideStruct *p3; // type case

}
};


int main (int argc, char * const argv[]) {
// insert code here...
std::cout << "Hello, Lucky World!\n";
class1<long> c1;
class2<double> c2;
std::cout << "class1 size: " << sizeof(c1) << "\n";
std::cout << "class2 size: " << sizeof(c2) << "\n";
c2.class2method();
return 0;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top