bug-gplusplus
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

incorrect scope of variables when using templates


From: Norbert Galm
Subject: incorrect scope of variables when using templates
Date: Tue, 19 Mar 2002 21:56:53 +0100

The following text reports a bug for the g++, version 2.95.3.
The compiler generates incorrect code (and messages) due to
faulty scoping of variables in the context of templates.
Consider the following file:

-- File "fs_a.cpp" -------------------------------

#include <iostream>

template <class T>
void Print (T t)
{
        i+=5;
}

int i=3;

int main ()
{
        //int i=3;
        //for (int i=3; i<10; ++i) /* do something or not */;

        double d;
        Print(d);
        cout << i << endl; // result with gcc, version 2.95.3 for unix: 8
        return 0;
}

--------------------------------------------------

The compilation of "fs_a.cpp" (fs = faulty scope) with g++, version 2.95.3
on a unix system, succeeds instead of giving a compile time error
(`i' is not defined within `Print'). The resulting executable prints "8".

Replacing the global definition of `i' by the local one (either outside
or inside the `for'-construct) results in the (correct) compile time error.
So the faulty scope does not occur in the case of local variables (so far - for
more details see below). Below it also becomes clear, why the latter two cases
(`for'-construct or not) are considered separately.

Consistently, replacing `i+=5;' in `Print' by `int i;' results in a (faulty)
compiler warning for the global case, saying that `Print::i' shadows `::i'.
For this, use the compiler option "-Wshadow".

-- File "fs_b.cpp" -------------------------------

template <class T>
void Print (T t)
{
        int i;
}

int i=3;

int main ()
{
        //int i=3;
        //for (int i=3; i<10; ++i) /* do something or not */;

        double d;
        Print(d);
        return 0;
}

--------------------------------------------------

Again, the problem does not occur for the two local cases.
All these problems occur only in the context of templates.
So far no local variable was shadowed. The next example shows
that this may also happen.

-- File "fs_c.cpp" -------------------------------

template <class T>
void Print (T t)
{
        int i;
}

template <class T>
struct Memory {
        T Data[10];
        virtual void Show ();
};

template <class T>
void Memory<T>::Show ()
{
        T t;
        for (int i=0; i<10; ++i) /* do something or not */;
        Print(t);
}

int main ()
{
        Memory<double> mem;
        return 0;
}

// end of file (line 27)

--------------------------------------------------

The command
        g++ fs_c.cpp -Wshadow
produces
        fs_c.cpp: In function `void Print<double>(double)':
        fs_c.cpp:18:   instantiated from `Memory<double>::Show()'
        fs_c.cpp:27:   instantiated from here
        fs_c.cpp:4: warning: declaration of `i' shadows previous local

Thus `Print::i' shadows `Memory::Show::i', which in fact is not the case for
two reasons:
 - Invoking g++ neither with "-ffor-scope" nor with "-fno-for-scope" should
   restrict the scope of `Memory::Show::i' to the `for' loop. This is actually
   the case, since it is not possible to access `i' outside the loop. (Try it.)
 - The obvious reason that `Print' is not in the scope of `Memory::Show'.

Surprisingly, replacing `int i;' in `Print' by `i+=5;' now results in a
compile time error and not in an incorrect executable as it was in the case
of a global `i'. The exact error message depends on whether the option
"-ffor-scope", "-fno-for-scope" or none of them is used.
The latter case (none is used) results in an interesting error message:
        fs_c.cpp: In function `void Print<double>(double)':
        fs_c.cpp:18:   instantiated from `Memory<double>::Show()'
        fs_c.cpp:27:   instantiated from here
        fs_c.cpp:4: name lookup of `i' changed for new ANSI `for' scoping
        fs_c.cpp:17:   using obsolete binding at `i'
        fs_c.cpp:4: use of `auto' variable from containing function
        fs_c.cpp:17:   `int i' declared here

What is a containing function? No function can contain other functions in C/C++.

Now back to the case of the definition `int i;' in `Print': The faulty warning
(`Print::i' shadows `Memory::Show::i') doesn't occur, if at least one of the
following is satisfied:
 1. Either the compiler option "-ffor-scope" or the option "-fno-for-scope"
    is used. The latter is the surprising case!!!
 2. The `for' statement in `Show' is replaced by `int i;'. In fact, this case
    is related to 1.
 3. The call of `Print' is contained in the `for' statement (just remove the
    semicolon at the end of the line).
 4. `Show' is non-virtual.
 5. `Mem' is no template class (replace the template paramter `T' by `double').
 6. `Print' is no template function (replace `T' by `double').

So the faulty warning occurs in the case of a `for' statement within a virtual
member function of a template class, if some other template function is called
(following the `for' statement) and the standard compiler treatment of
`for'-init-statements is used. These are rather special circumstances.
But I had them in my code, before I knew these compiler options
(e.g. in a matrix library containing several template classes and functions).

Another, analogous circumstance for this faulty warning is, when the
`for' statement is contained in a template function:

-- File "fs_d.cpp" -------------------------------

template <class T>
void Print (T t)
{
        int i;
}

template <class T>
void P (T t)
{
        for (int i=0; i<10; ++i) /* do something or not */;
        Print(t);
}

int main ()
{
        double d;
        P(d);
        return 0;
}

--------------------------------------------------

To summarize:

In the case of a global variable ("fs_a/b.cpp"), there is a real bug, which has
to be fixed (or may already be fixed).
In the case of a local variable, one might argue that using "-ffor-scope" is the
only truth for the future (in fact, in the meantime I'm using it consequently)
and only faulty warnings and no incorrect code is generated. But there is a bug,
which may have other effects not recognized so far.


One last curious observation:

The warning, that `Print::i' shadows `Memory::Show::i' contains information,
in which lines the template instantiations are done. This is incorrect!!!
The instantiation of `Memory<double>' is not in line 27 (see "fs_c.cpp").
Here the compiler incorrectly reports the end of the file.


Thanks for any comments.

    Norbert






reply via email to

[Prev in Thread] Current Thread [Next in Thread]