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 typeC
, the unqualified-id is looked up in the scope of classC
.
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
Register an Object Creator in Object Factory
0Xc0000005: Access Violation Reading Location 0X00000000
Why Are Override and Final Identifiers with Special Meaning Instead of Reserved Keywords
When Did "And" Become an Operator in C++
How to Create a N Way Cartesian Product of Type Lists in C++
What Happens When Calling the Destructor of a Thread Object That Has a Condition Variable Waiting
Pass by Reference More Expensive Than Pass by Value
Gcc Does Not Honor 'Pragma Gcc Diagnostic' to Silence Warnings
What Does the C++ New Operator Do Other Than Allocation and a Ctor Call
Factor a Large Number Efficiently with Gmp
Why How to Implicitly Convert an Int Literal to an Int * in C But Not in C++
Initialising Reference in Constructor C++
How to Format a Datetime to String Using Boost
Do You Prefer Explicit Namespaces or 'Using' in C++
Specializing Single Method in a Big Template Class
Why Doesn't the Program Crash When I Call a Member Function Through a Null Pointer in C++