views:

202

answers:

3

Usually, when A is inheriting from B, all the members of A are automatically visible to B's functions, for example

class A {
protected:
    int a;
};

class B : public A {
    int getA() {return a;}
    //no need to use A::a, it is automatically visible
};

However when I'm inheriting with templates, this code becomes illegal (at least in gcc)

template<typename T>
class A {
protected:
    int a;
};

template<typename T>
class B : public A<T> {
    int getA() {return a;}
};

templt.cpp: In member function `int B<T>::getA()':
templt.cpp:9: error: `a' undeclared (first use this function)
templt.cpp:9: error: (Each undeclared identifier is reported only once for each
function it appears in.)

I must do one of

class B : public A<T> {
    using B::a;
    int getA() {return a;}
};

class B : public A<T> {
    using A<T>::a;
    int getA() {return a;}
};

class B : public A<T> {
    int getA() {return B::a;}
};

etc. As if the variable a is hidden by another variable of B, in the following case:

class HiddenByOverload {void hidden(){}}
class HidesByOverload : public HiddenByOverload {
    void hidden(int i) {} //different signature, now `hidden` is hidden
    void usehidden() {
        HiddenByOverload::hidden(); // I must expose it explicitly
    }
}

Why is it so? Are there any other ways to prevent C++ from hiding the parent template class' variables?

Edit: thanks for everyone for the fascinating discussion. I must admit I didn't follow the argument which quoted paragraphs from the C++ standard. It's hard for me to follow it without reading the actual source.

The best thing I can do to summarize the discussion, is quoting a short line from "The Zen of Python":

If the implementation is hard to explain, it's (probably) a bad idea.

+5  A: 

You can also do

class B : public A<T> {
    int getA() {return this->a;}
};

The problem is that the member is in a base, which depends on a template parameter. Normal unqualified lookup is performed at the point of definition, not at the point of instantiation, and therefore it doesn't search dependent bases.

avakar
I now understand why this happens. But I don't get why is the standard designed in such a way. If I'm in a template, why would I want NOT to lookup things in the template instantiation. The only thing I can think about is, that it makes implementing the compiler easier.
Elazar Leibovich
Minor nit: 14.6.2 Dependent names: Para 3: In the definition of a class template or a member of a class template, if a base class of the class templatedepends on a template-parameter, the base class scope is *not* examined during unqualified name lookupeither at the point of definition of the class template or member *or during an instantiation of the classtemplate or member.*
dirkgently
The standard seems to foresee that templates are parsed twice: during the first pass the compiler checks anything non-dependent, when instantiated it also checks the dependent things. (But VC++ seems to largely ignore the first pass, and accept your code.)
UncleBens
@dirkgently, interesting. I wander why the clause is there, since there is no unqualified lookup performed at the point of intantiation (as far as I can tell). Unqualified names are bound at the point of definition.
avakar
See the problem report i just sent to clang: http://llvm.org/bugs/show_bug.cgi?id=6532
Johannes Schaub - litb
So we not only ignore dependent bases for unqualified name lookup at the toplevel of name lookup, but also during unqualified name lookup as part of other forms of lookup (in the code i've baken, unqualified lookup appears as part of class-member-access lookup: This is known as "lookup in the context of the expression" (3.4/2), and is used in various places across the Standard).
Johannes Schaub - litb
@UncleBens as I said, I don't understand why should anything be checked at the first pass. If I understand it correctly, `A<T>` (instantized) will inherit from anything `A` (uninstantized) would.Single or double pass is a matter for the implementation I think. But as you implied, it could be that the standard was made to make implementation easier.
Elazar Leibovich
@litb, you didn't make it clearer at all. :) But thank you, I'll try to browse through it when I have time.
avakar
@avakar, see the longer explanation below. should make it clearer than the above comment (which was confusing, i think).
Johannes Schaub - litb
I found the defect report that is responsible for forbidding unqualified names from being looked up in dependent bases: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#213 . I quoted and discussed the DR in the GCC bugreport i mentioned in the comments to the question above. I think this is an interesting matter.
Johannes Schaub - litb
+2  A: 

It's a common issue, it is however perfectly possible to circumvent it, be it for functions, types or attributes.

The problem comes up with the implementation of the 2 phases evaluation of template classes and functions. Normally the standard requires that the templates be evaluated two times:

  • A first time when encountered, to validate that it's correctly formed
  • The second when instanciated, to actually produce the code for the types given

During the first evaluation, the template parameters are unknown, so it's impossible to tell what the base class is going to be... and notably if it contains a a member. Any symbol that does not depend on one of the template parameters is expected to be clearly defined and will be checked.

By explicitly defining the scope you will delay the check to the second evaluation by making the symbol dependent on template parameters.

Using Boost as inspiration:

template <class A1, class A2, class A3>
class MyClass: public Base<A1,A2,A3>
{
public:
  typedef Base<A1,A2,A3> base_;

  void foo()
  {
    // Accessing type
    bar_type x;             // ERROR: Not dependent on template parameter
    typename base_::bar_type x;

    // Accessing method
    bar();                  // ERROR: Not dependent on template parameter
    this->bar();
    base_::bar();

    // Accessing attribute
    mBar;                   // ERROR: Not dependent on template parameter
    this->mBar;
    base_::mBar;
  };

}; // class MyClass

I like the Boost idiom of defining a base_ inner typedef. First it certainly helps to define the constructors and second by explicitly qualifiying what comes from the Base class it makes things clear for those treading through the code.

Matthieu M.
+3  A: 

Since there are questions about how unqualified names may be dependent, or how unqualified name lookup may apply to dependent names:


Trail parse: Determining how we parse a statement

If in a template a dependent name is encountered, it is always assumed not to name a type, unless the name lookup that is applicable finds that it is a type or we prepend the name with typename:

template<typename T>
void f() {
  T f0; // T is a template type parameter => type
  T *f1;

  typename T::name g1; // "name" is assumed to be a type. 
  T::name g0; // "name" cannot be looked up here => non-type
}

This lookup of a name to determine whether it is a type is always done at the point of the template definition for all dependent names: It guides the following parse into a certain direction. In the second statement, we will parse T *f1 as a declaration of a pointer, but not as a multiplication. In the last statement, we assumed during the pre-parse disambiguation that T::name is not a type and try to parse it as an expression. This will fail, because we will expect a semicolon or some operator after T::name. This lookup whether or not the name is a type has no influence on the meaning of the name in later phases: It won't yet bind the name to any declaration.


Actual parse

After we determined what names are types and what not, we will actually parse the template. Names that are not dependent - that is, those that are not looked up in a scope that is dependent or that are not explicitly made dependent by other rules - are looked up at the point where they are used in the template, and their meaning is not influenced by any declaration visible when instantiating.

Names that are dependent are looked up when instantiating, both in the template definition where they are used, and where their template is instantiated. This is true also for unqualified names that are dependent:

template<typename T>
struct Bar {
  void bar() { foo(T()); }
};

namespace A {
  struct Baz { };
  void foo(Baz); // found!
}

int main() { Bar<A::Baz> b; b.bar(); }

The unqualified foo is made dependent by the Standard because the argument T() is type-dependent. When instantiating, we will look for functions called foo using unqualified name lookup around the template definition, and using argument dependent lookup (meaning, roughly, in the namespace of T) around both the template definition and the point where we instantiate it (after main). Argument dependent lookup will then find foo.

If Bar now has a dependent base class, unqualified lookup must ignore that dependent base class:

template<typename T>
struct HasFoo { };

template<typename T>
struct Bar : HasFoo<T> {
  void bar() { foo(T()); }
};

namespace A {
  struct Baz { };
  void foo(Baz); // found!
}

template<>
struct HasFoo<A::Baz> {
  void foo();
};

int main() { Bar<A::Baz> b; b.bar(); }

This must still find A::foo, despite the fact that unqualified name-lookup would find a class member function if it were done (ADL will not find additional functions if a class member function was found). But unqualified namelookup will not find that function, because it's a member of a dependent base class, and those are ignored during unqualified name lookup. Another interesting case:

template<typename>
struct A {
  typedef int foo;
  operator int() {
    return 0;
  }
};

// makes sure applicable name-lookup
// classifies "foo" as a type (so parsing will work).
struct TypeNameSugar {
  typedef int foo;
};

template<typename T>
struct C : A<T>, TypeNameSugar {
  void c() {
    A<T> *p = this;
    int i = p->operator foo();
  }
};

int main() {
  C<void>().c();
}

The Standard states that during lookup of foo in the operator foo name, we will lookup independently in both the scope of p-> (which is in the scope of class A<T>), and the scope in which the complete expression appears (which is the scope of C<T>::c as an unqualified name), and compare the two names, if found, whether they designate the same type. If we would not ignore the dependent base class A<T> during the lookup in the scope of the complete expression, we will find foo in two base classes, thus having an ambiguity. Ignoring A<T> will mean we find the name once when we do lookup into p->, and another time when we do lookup into TypeNameSugar.

Johannes Schaub - litb