[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c++

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

jdmuys

10/8/2008 11:24:00 AM

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;
}
6 Answers

Marcel Müller

10/8/2008 11:39:00 AM

0

Hi,

jdmuys schrieb:
> 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<Type>::insideStruct *p2;

> };

Implicit typenames in templates are deprecated for a while.


Marcel

Leandro Melo

10/8/2008 11:45:00 AM

0

On 8 out, 08:24, jdmuys <jdm...@gmail.com> wrote:
> 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;


--
Leandro T. C. Melo


jdmuys

10/9/2008 6:53:00 AM

0

On Oct 8, 1:45 pm, Leandro Melo <ltcm...@gmail.com> wrote:

> 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

Juha Nieminen

10/9/2008 11:27:00 AM

0

jdmuys wrote:
> 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*?)

James Kanze

10/10/2008 8:11:00 AM

0

On Oct 9, 1:26 pm, Juha Nieminen <nos...@thanks.invalid> wrote:
> jdmuys wrote:
> > 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*?)

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.

--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

jdmuys

10/10/2008 12:22:00 PM

0

On Oct 9, 1:26 pm, Juha Nieminen <nos...@thanks.invalid> wrote:
> jdmuys wrote:
> > 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*?)

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;
}