Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does C++ name lookup seem inconsistent in this example?

Tags:

c++

I have following C++ code:

template <typename Type, typename T>
class zz
{
};

class foo
{
  template <typename T>
  using zz = ::zz<foo, T>;
  
  struct own_type : zz<double>
  {
    own_type():
      zz<foo, double>{} {} // ERROR: needs foo arg !!
  };
  
  template <typename T>
  struct zz_type_gen : zz<T>
  {
    zz_type_gen():
      zz<T>() {}
  };

  zz<int> _oi;
  zz_type_gen<char> _od;
};

Compiling with g++ 11, clang++ 12 and cl.exe with -std=c++20 works fine but if foo template argument is removed in line with // ERROR comment compilation fails. This seems to indicate that zz name is looked up first in global namespace inside nested class in case nested class (own_type) is not template. However zz name is looked up first in class foo in case nested class is template (zz_type_gen). I could not find clear explanation on how C++ names lookup works, but intuitively this seems inconsistent.

like image 858
Joseph Avatar asked Oct 27 '22 10:10

Joseph


1 Answers

To allow

namespace N {
  struct A {int f();};
}
struct B : N::A {
  int f() {return A::f()+1;}
};

without repeating the namespace qualification of A, and for the similar case of not repeating the template arguments of a class template, each class is considered to declare itself as a member (although this syntax is also used to declare or inherit constructors).

To prevent (specializations of) dependent base classes from shadowing names used in templated classes, unqualified name lookup is performed immediately in the template definition and ignores such bases entirely. This has the odd interaction with the injected-class-name model that even a name that would obviously refer to the base in each instantiation is sought outside the class instead. This does provide consistency with the case where the dependent base is more complicated than a template-id that names some specialization of a particular class template.

Therefore, own_type finds the injected-class-name of its base, which can be used either as a type-name (by omitting <foo, double>) or as the template-name that ::z is. zz_type_gen, however, finds foo::zz; it could use typename zz_type_gen::zz to find the injected-class-name instead of repeating the template argument list. (C++20 dispenses with the requirement for typename in some contexts but not others; a mem-initializer still requires it, but that was likely an oversight.)

like image 167
Davis Herring Avatar answered Nov 16 '22 06:11

Davis Herring