What Is the Rule That Allows 'This->' to Access Members of Dependent Base Classes

What is the rule that allows `this-` to access members of dependent base classes?

Class member access expressions (5.2.5. [expr.ref]) don't use unqualified lookup rules, they use class member access lookup rules (3.4.5 [basic.lookup.classref]).

(2) If the id-expression in a class member access (5.2.5) is an unqualified-id, and the type of the object expression
is of a class type C, the unqualified-id is looked up in the scope of class C.

Is it a defect when the base class is a dependent type

According to C++ Defect Report Support in Clang, currently (2020-07-06) Clang does not implement the resolution of CWG591, where the paragraph with the definition of dependent base class and the Example you cite in the question was added.

ISO C++ Standard - rules regarding examining dependent base. Why?

Because templates might be specialized later. e.g.

template<typename T>
struct B {
int f();
};

template<typename T>
struct D : B<T> {
int g();
};

template<typename T>
int D<T>::g() {
return f(); // the name f won't be looked up when not knowing the exact type of T
}

template<>
struct B<int> {
// no function named f for B<int>
};

So standard C++ says that nondependent names are not looked up in dependent base classes.

Adding this-> makes the name dependent and dependent names can be looked up only at the time of instantiation, and at that time the exact base specialization that must be explored will be known.

Also refer to Two Phase Lookup.

When/why/how do unqualified names look in dependent base?

Nothing has changed. The relevant rule is now [class.member.lookup]/4:

Calculate the lookup set for N in each direct non-dependent ([temp.dep.type]) base class […]

so that there need not be a special override for the name-lookup rules in [temp].

Derived template-class access to base-class member-data

You can use this-> to make clear that you are referring to a member of the class:

void Bar<T>::BarFunc () {
std::cout << this->_foo_arg << std::endl;
}

Alternatively you can also use "using" in the method:

void Bar<T>::BarFunc () {
using Bar<T>::_foo_arg; // Might not work in g++, IIRC
std::cout << _foo_arg << std::endl;
}

This makes it clear to the compiler that the member name depends on the template parameters so that it searches for the definition of that name in the right places. For more information also see this entry in the C++ Faq Lite.

When should I make explicit use of the `this` pointer?

Usually, you do not have to, this-> is implied.

Sometimes, there is a name ambiguity, where it can be used to disambiguate class members and local variables. However, here is a completely different case where this-> is explicitly required.

Consider the following code:

template<class T>
struct A {
T i;
};

template<class T>
struct B : A<T> {
T foo() {
return this->i; //standard accepted by all compilers
//return i; //clang and gcc will fail
//clang 13.1.6: use of undeclared identifier 'i'
//gcc 11.3.0: 'i' was not declared in this scope
//Microsoft C++ Compiler 2019 will accept it
}

};

int main() {
B<int> b;
b.foo();
}

If you omit this->, some compilers do not know how to treat i. In order to tell it that i is indeed a member of A<T>, for any T, the this-> prefix is required.

Note: it is possible to still omit this-> prefix by using:

template<class T>
struct B : A<T> {
int foo() {
return A<T>::i; // explicitly refer to a variable in the base class
//where 'i' is now known to exist
}

};

Accessing types from dependent base classes

As Richard Corden points out, this issue was addressed in the C++ Standard Core Language Defect Reports after the 2003 standard was ratified: How do the keywords typename/template interact with using-declarations?

Proposed resolution (April 2003,
revised October 2003):

Add a new paragraph to the bottom of
7.3.3 [namespace.udecl]:

If a using-declaration uses the
keyword typename and specifies a
dependent name (14.7.2 [temp.dep]),
the name introduced by the
using-declaration is treated as a
typedef-name (7.1.3 [dcl.typedef]).

This text doesn't seem to appear in the Second Edition standard from October 15, 2003.

GCC does not yet implement this resolution, as explained in bug 14258:

------- Comment #3 From Giovanni Bajo 2004-02-27 12:47 [reply] ------- The
problem is that our USING_DECL doesn't
record the "typename", that is the
fact that it is a type which is
imported through it. This used to work
thanks to the implicit type name
extension, I believe.

Duplicate bug 21484 indicates that 'using typename' works on Comeau and Intel compilers. Because MSVC treats all names as dependent, the construct is unnecessary (but permitted) for that compiler.


Fixed in GCC 4.7 on Dec 13 2011!



Related Topics



Leave a reply



Submit