Suppose I have the templated classes
#include <iostream>
class A1 {
public:
int x{314159};
};
template<typename Context>
class A2 : public Context {};
template<typename Context>
class A3 : public Context {};
template<typename Context>
class A4 : public Context {
public:
int func() {
return Context::A1::x;
}
int gunc() {
return this->A1::x;
}
int hunc() {
return A1::x;
}
};
int main() {
A4<A3<A2<A1>>> my_A;
std::cout << "x = func() = " << my_A.func() << std::endl;
std::cout << "x = gunc() = " << my_A.gunc() << std::endl;
std::cout << "x = hunc() = " << my_A.hunc() << std::endl;
return 0;
}
Inside the definition of the templated class A4, at least when only the instance type A4<A3<A2<A1>>> is used, it seems to be possible to refer to x as either
this->A1::x;
or
Context::A1::x;
or
A1::x;
Question 1: Are these equivalent? Well, I think I can see them not being equivalent from the point of view of the templated class A4 viewed in isolation. For Context::A1::x to work its template parameter should contain an x. For this->A1::x to work it should contain a scope called A1, which in turn should contain an x. And for A1::x to work the scope of A4 itself should contain a scope called A1 containing an x. My intention is to ask if they are equivalent from the point of view of the type A4<A3<A2<A1>>>.
Nota bene: gcc 8.2 with -O03 -std=c++17 produces the same assembly code in each case. Namely, I compiled the code with only one of the functions func, gunc, and hunc and only one call to the corresponding one, and this compiler produced identical executables. Of course, strictly speaking this doesn't necessarily imply that for the language in abstract those expressions are equivalent.
Question 2: How does the 'unpacking' of the scope of x works in each case? Maybe this question doesn't make sense or is not exactly what I want to ask. Specially if the answer to Question 1 is that they are equivalent. Allow me modify this question after I find more information about Question 1, or ignore this question at first.
Note to Question 2: This observation might clarify why I am unsure how the unpacking works. If in the templated class A4 we had one more method
int iunc() {
return Context::Context::A1::x;
}
then the compilation fails with
memberTemplatedParent.cpp: In instantiation of ‘int A4<Context>::iunc() [with Context = A3<A2<A1> >]’:
memberTemplatedParent.cpp:48:45: required from here
memberTemplatedParent.cpp:37:22: error: no type named ‘Context’ in ‘class A3<A2<A1> >’
return Context::Context::A1::x;
^
So, at least for gcc at the moment that the type instance of A4 is being created, the template parameter of its template parameter is not a valid name (or I didn't name it properly in Context::Context::A1::x).
Questions 1 and 2:
All versions are equivalent for the instantiation you have chosen. As long as it is not ambiguous, you can use the member
xdirectly without specifying the scope. If the member is not in the current class, the base class is checked, and so further.If you specify a particular base class and the member
xis not there, again the base class is consulted.For your particular specialization, you have
};
Last question:
reads as follows after template instanciation
The compiler now complains that there is no typedef in the class
A3<A2<A1>>which introduces the nameContext. The template parameter is only visible within the class template.