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

Discussion in 'C++' started by jdmuys, Oct 8, 2008.

  1. jdmuys

    jdmuys Guest

    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;
    }
    jdmuys, Oct 8, 2008
    #1
    1. Advertising

  2. 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
    Marcel Müller, Oct 8, 2008
    #2
    1. Advertising

  3. jdmuys

    Leandro Melo Guest

    On 8 out, 08:24, jdmuys <> 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
    Leandro Melo, Oct 8, 2008
    #3
  4. jdmuys

    jdmuys Guest

    On Oct 8, 1:45 pm, Leandro Melo <> 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
    jdmuys, Oct 9, 2008
    #4
  5. 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*?)
    Juha Nieminen, Oct 9, 2008
    #5
  6. jdmuys

    James Kanze Guest

    On Oct 9, 1:26 pm, Juha Nieminen <> 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:
    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
    James Kanze, Oct 10, 2008
    #6
  7. jdmuys

    jdmuys Guest

    On Oct 9, 1:26 pm, Juha Nieminen <> 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;
    }
    jdmuys, Oct 10, 2008
    #7
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. James Harris
    Replies:
    4
    Views:
    1,382
    James Harris
    Oct 9, 2003
  2. Chris Fogelklou
    Replies:
    36
    Views:
    1,370
    Chris Fogelklou
    Apr 20, 2004
  3. verec
    Replies:
    5
    Views:
    3,022
    verec
    Aug 16, 2005
  4. =?ISO-8859-1?Q?Martin_J=F8rgensen?=

    what's the difference between type struct and type class exactly?

    =?ISO-8859-1?Q?Martin_J=F8rgensen?=, Feb 22, 2006, in forum: C++
    Replies:
    5
    Views:
    502
    =?ISO-8859-1?Q?Martin_J=F8rgensen?=
    Feb 22, 2006
  5. Replies:
    2
    Views:
    733
    David Harmon
    Sep 20, 2006
Loading...

Share This Page