49

Why can't a C++ compiler recognize that g() and b are inherited members of Superclass as seen in this code:

template<typename T> struct Superclass {
 protected:
  int b;
  void g() {}
};

template<typename T> struct Subclass : public Superclass<T> {
  void f() {
    g(); // compiler error: uncategorized
    b = 3; // compiler error: unrecognized
  }
};

If I simplify Subclass and just inherit from Subclass<int> then it compiles. It also compiles when fully qualifying g() as Superclass<T>::g() and Superclass<T>::b. I'm using LLVM GCC 4.2.

Note: If I make g() and b public in the superclass it still fails with same error.

andrewz
  • 4,729
  • 5
  • 49
  • 67
  • 9
    This happens because of the two-phase name lookup (which not all compilers use by default). There are 4 solutions to this problem: **1)** Use the prefixes `Superclass::b` and `Superclass::g()`, **2)** Use the prefixes `this->a` and `this->g()`, **3)** Add statements `using Superclass::a` and `using Superclass::g`, **4)** Use a global compiler switch that enables the permissive mode. The pros & cons of these solutions are described in https://stackoverflow.com/questions/50321788/a-better-way-to-avoid-public-member-invisibility-and-source-code-bloat-repetitio – George Robinson May 14 '18 at 13:47

2 Answers2

70

This can be amended by pulling the names into the current scope using using:

template<typename T> struct Subclass : public Superclass<T> {
  using Superclass<T>::b;
  using Superclass<T>::g;

  void f() {
    g();
    b = 3;
  }
};

Or by qualifying the name via the this pointer access:

template<typename T> struct Subclass : public Superclass<T> {
  void f() {
    this->g();
    this->b = 3;
  }
};

Or, as you’ve already noticed, by qualifying the full name.

The reason why this is necessary is that C++ doesn’t consider superclass templates for name resolution (because then they are dependent names and dependent names are not considered). It works when you use Superclass<int> because that’s not a template (it’s an instantiation of a template) and thus its nested names are not dependent names.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 8
    Microsoft's compilers fail to obey this rule. I am angry – Armen Tsirunyan Oct 24 '10 at 21:06
  • 11
    @Armen: only this one rule? Wow, it really *has* gotten better. :-D – Konrad Rudolph Oct 25 '10 at 06:04
  • `this` pointer solution works OK for me with stock g++ on Mac 10.7. – simpzan Jun 01 '13 at 08:42
  • 2
    Note that you can alternatively qualify with `Subclass::Superclass::` as well, if the template parameters are excessive. – Potatoswatter Jun 01 '13 at 09:17
  • I kinda like that Microsoft don't obey this rule. I really hope there is an option to delay all those symbol check of template until instantiation. It will significantly improve the expressiveness of template and reduce many syntax noise. – JAH May 08 '17 at 15:44
18

Konrad's answer doesn't ask or answer the final "why" in all of this. It's not just the C++ committee arbitrarily saying "hey, give up on dependent names, nobody likes them anyway". Rather, the compiler does some checking of the templates even before they're instantiated, and it can't make any sense of g() or b until it knows T, as it can't - in general - select between possible specialisations of the base class (e.g. SuperClass<X> may have int b while SuperClass<Y> has void b() and SuperClass<Z> doesn't have b at all). The more explicit forms are just saying "trust me - this must come from the base class at instantiation time (otherwise there'll be a compiler error then)".

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • 1
    Kinda makes sense, but it definitely is one of the more obscure "features" of templating. – ChrisWue Jan 08 '14 at 22:06
  • @ChrisWue: it's got plenty of competition in that race! ;-) – Tony Delroy Jan 09 '14 at 02:47
  • GCC _was_ able to handle this in the past (1990s), but later as C++ evolved it was changed. I assume the way it was handled caused problems in some cases, which is why it was changed. – PolarBear2015 Feb 06 '17 at 14:32