Search code examples
c++templatesc++17dependent-namename-hiding

Name resolution when a structure declaration is hidden by a variable


Let's consider the following demonstrative program.

#include <iostream>

struct A
{
    struct B 
    {
        int b = 10;
    };
    int B = 20;
};

template <class T>
struct C
{
    void f() const
    {
        typename /*struct*/ T::B b;
        int x = b.b;
        std::cout << "x == " << x << '\n';
    }
};

int main()
{
    C<A>().f();
}

As it is seen the declaration of the member struct B in the structure A is hidden by the declaration of the data member B having the type int.

So in the function definition of the template structure declaration

typename /*struct*/ T::B b;

the depended name T::B should not be found.

However the compiler gcc 8.3 compiles the program successfully and the program outputs

x == 10

On the other hand, the compiler Visual C++ 2019 is not going to compile the program and issues a syntax error.

So is it a bug of the compiler gcc 8.3?

The second problem is that it would be natural if such a construction (with uncommented keyword struct) with an elaborated type specifier would be allowed.

typename struct T::B b;

However the both compilers consider the construction as incorrect.

Is it indeed incorrect?


Solution

  • It should be a bug of the compiler gcc 8.3. Because the standard rule that typename T::B shall be resolved to variable rather than struct B, these rules are listed in the following:

    A class name or enumeration name can be hidden by the name of a variable, data member, function, or enumerator declared in the same scope. If a class or enumeration name and a variable, data member, function, or enumerator are declared in the same scope (in any order) with the same name, the class or enumeration name is hidden wherever the variable, data member, function, or enumerator name is visible.

    The above rule says that the name of struct B was hidden by the name of declaration int B = 20;

    When a qualified-id is intended to refer to a type that is not a member of the current instantiation ([temp.dep.type]) and its nested-name-specifier refers to a dependent type, it shall be prefixed by the keyword typename, forming a typename-specifier. If the qualified-id in a typename-specifier does not denote a type or a class template, the program is ill-formed.

    The usual qualified name lookup is used to find the qualified-id even in the presence of typename.

    The above rule says that the keyword typename does not affect the result of qualified name lookup, In other words, the keyword typename does not require that the name lookup procedure only finds types.

    So, in the scope of A, the declarations of int B = 20; and struct B are all found, and then the variable hides class name. So, according to rule If the qualified-id in a typename-specifier does not denote a type or a class template, the program is ill-formed, the program should be ill-formed. Hence, GCC 8.3 is wrong.

    In addition, not only GCC 8.3 but also the higher versions are all wrong.