c++ - Why do I have to access template base class members through the this pointer? -


if classes below not templates have x in derived class. however, code below, have to use this->x. why?

template <typename t> class base {  protected:     int x; };  template <typename t> class derived : public base<t> {  public:     int f() { return this->x; } };  int main() {     derived<int> d;     d.f();     return 0; } 

short answer: in order make x dependent name, lookup deferred until template parameter known.

long answer: when compiler sees template, supposed perform checks immediately, without seeing template parameter. others deferred until parameter known. it's called two-phase compilation, , msvc doesn't it's required standard , implemented other major compilers. if like, compiler must compile template sees (to kind of internal parse tree representation), , defer compiling instantiation until later.

the checks performed on template itself, rather on particular instantiations of it, require compiler able resolve grammar of code in template.

in c++ (and c), in order resolve grammar of code, need know whether type or not. example:

#if want_pointer     typedef int a; #else     int a; #endif static const int x = 2; template <typename t> void foo() { *x = 0; } 

if type, declares pointer (with no effect other shadow global x). if object, that's multiplication (and barring operator overloading it's illegal, assigning rvalue). if wrong, error must diagnosed in phase 1, it's defined standard error in template, not in particular instantiation of it. if template never instantiated, if int above code ill-formed , must diagnosed, if foo wasn't template @ all, plain function.

now, standard says names aren't dependent on template parameters must resolvable in phase 1. a here not dependent name, refers same thing regardless of type t. needs defined before template defined in order found , checked in phase 1.

t::a name depends on t. can't possibly know in phase 1 whether that's type or not. type used t in instantiation quite isn't defined yet, , if don't know type(s) used our template parameter. have resolve grammar in order our precious phase 1 checks ill-formed templates. standard has rule dependent names - compiler must assume they're non-types, unless qualified typename specify are types, or used in unambiguous contexts. example in template <typename t> struct foo : t::a {};, t::a used base class , hence unambiguously type. if foo instantiated type has data member a instead of nested type a, that's error in code doing instantiation (phase 2), not error in template (phase 1).

but class template dependent base class?

template <typename t> struct foo : bar<t> {     foo() { *x = 0; } }; 

is a dependent name or not? base classes, any name appear in base class. dependent name, , treat non-type. have undesirable effect every name in foo dependent, , hence every type used in foo (except built-in types) has qualified. inside of foo, you'd have write:

typename std::string s = "hello, world"; 

because std::string dependent name, , hence assumed non-type unless specified otherwise. ouch!

a second problem allowing preferred code (return x;) if bar defined before foo, , x isn't member in definition, later define specialization of bar type baz, such bar<baz> have data member x, , instantiate foo<baz>. in instantiation, template return data member instead of returning global x. or conversely if base template definition of bar had x, define specialization without it, , template global x return in foo<baz>. think judged surprising , distressing problem have, it's silently surprising, opposed throwing surprising error.

to avoid these problems, standard in effect says dependent base classes of class templates aren't searched names unless names dependent other reason. stops being dependent because found in dependent base. has undesirable effect you're seeing - have qualify stuff base class or it's not found. there 3 common ways make a dependent:

  • using bar<t>::a; in class - a refers in bar<t>, hence dependent.
  • bar<t>::a *x = 0; @ point of use - again, a in bar<t>. multiplication since typename wasn't used, possibly bad example, we'll have wait until instantiation find out whether operator*(bar<t>::a, x) returns rvalue. knows, maybe does...
  • this->a; @ point of use - a member, if it's not in foo, must in base class, again standard says makes dependent.

two-phase compilation fiddly , difficult, , introduces surprising requirements verbiage in code. rather democracy it's worst possible way of doing things, apart others.

you reasonably argue in example, return x; doesn't make sense if x nested type in base class, language should (a) it's dependent name , (2) treat non-type, , code work without this->. extent you're victim of collateral damage solution problem doesn't apply in case, there's still issue of base class potentially introducing names under shadow globals, or not having names thought had, , global being found instead.

you possibly argue default should opposite dependent names (assume type unless somehow specified object), or default should more context sensitive (in std::string s = "";, std::string read type since nothing else makes grammatical sense, though std::string *s = 0; ambiguous). again, don't know quite how rules agreed. guess number of pages of text required, mitigated against creating lot of specific rules contexts take type , non-type.


Comments

Popular posts from this blog

java - Intellij Synchronizing output directories .. -

git - Initial Commit: "fatal: could not create leading directories of ..." -