11

The following code can be compiled without error:

template <typename T> struct A {
    void f() { this->whatever; } // whatever is not declared before
};
int main() {
    A<int> a;
}

And I know it's because this is a type-dependent expression, which makes name lookup for whatever is postponed until the actual template argument is known. Since member function f() is never used in this case, so that no instantiation of A<T>::f exists, and name lookup for whatever is never performed.

I can understand that this is type-dependent if the class template has a type-dependent base like:

template <typename T> struct B { T whatever; };
template <typename T> struct A : B<T> {
    void f() { this->whatever; }
};
int main() {
    A<int> a;
}

When parsing the definition of template class A, it's impossible to know what's type of its base, which makes this->whatever potentially legal (B<T> could has a member named whatever). On the contrary, I haven't see any potential that this->whatever would be legal in the first example as soon as member function f is used somewhere.

So, could this->whatever be legal at some points in the first example? If not, is there any other reason that this should be treated as type-dependent expression in that case?

Carousel
  • 728
  • 4
  • 13
  • 3
    In your case, type of `this` is always going to be `A* const` in `A::f()`. I don't understand your confusion. – R Sahu Aug 30 '16 at 04:41
  • 3
    @RSahu I think the question is why the language defines `this` to be type-dependent in any template context, not just ones where it might actually be type-dependent. – templatetypedef Aug 30 '16 at 04:43
  • 1
    @RSahu I mean if `this` is not treated as type-dependent in the first case, shouldn't it be less error-prone? As my understanding, one of the reason that un-dependent name is lookup when parsing the definition of the template instead of when instantiating the template is to make code less error-prone. – Carousel Aug 30 '16 at 04:46
  • @Carousel, I see your point. – R Sahu Aug 30 '16 at 04:50
  • 2
    @Carousel, whether `this` is type dependent or not, generating code for functions for a template instantiation is the only way a compiler can detect whether the code in a function is well formed or not. You are asking why the compiler does't generate code for `A:f()` even though it is not used. – R Sahu Aug 30 '16 at 04:55
  • 1
    The type, `A`, of `this` clearly depends on `T`. – molbdnilo Aug 30 '16 at 06:10
  • 1
    @molbdnilo well, maybe I should change my question to "Why dependent name should always be lookuped until the actual template argument is known?" – Carousel Aug 30 '16 at 06:22
  • Because this rule is relatively simple and useful in practice. – n. m. could be an AI Aug 30 '16 at 06:23

3 Answers3

4

Your code is "ill-formed, no diagnostic required", because there is never a valid specialization for A::f. In fact, the spec says that this->whatever is neither a member of an unknown specialization (because there is no dependent base class), nor a member of the current instantiation (because it's not declared in a non-dependent base class, nor in the class template itself). This in addition renders your code invalid, and again no diagnostic is required (but allowed). This is explained in more details at https://stackoverflow.com/a/17579889/34509

this is type-dependent because you don't know the template parameter values yet in the definition. So for instance SomeOtherTemplate<decltype(*this)> cannot be resolved immediately, but needs to wait till the class template of this is instantiated (so you need a typename before SomeOtherTemplate<decltype(*this)>::type).

However, just because this is type dependent, doesn't mean that this->whatever is aswell. As described above, the spec has tools to correctly categorize this as invalid, and in fact also does not make this->whatever type dependent. It says

A class member access expression ([expr.ref]) is type-dependent if the expression refers to a member of the current instantiation and the type of the referenced member is dependent, or the class member access expression refers to a member of an unknown specialization.

Community
  • 1
  • 1
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
0

Your example can be simplified even further:

template <typename T> struct A {
    void f() { this = 1; }
};
int main() {
    A<int> a;
}

The statement this = 1; should never compile and that can't be fixed even if A<T> has a type-dependent base class. However the compiler doesn't complain until the function A<T>::f() is instantiated.

As Johannes Schaub - litb has already answered this may be a "no diagnostic required" situation.

Community
  • 1
  • 1
Leon
  • 31,443
  • 4
  • 72
  • 97
0

This is the name lookup rules about dependent names.

$14.6/9 Name resolution [temp.res]:

When looking for the declaration of a name used in a template definition, the usual lookup rules ([basic.lookup.unqual], [basic.lookup.argdep]) are used for non-dependent names. The lookup of names dependent on the template parameters is postponed until the actual template argument is known ([temp.dep]).

The intent is, the information is not sufficient if the name depends on template parameter, until the actual template argument is known. The complier won't distinguish the dependent names' type (formed by this or others), won't check the details like the class has a dependent base class or not. The result might not change like sample code you showed, but it just postpones the name lookup until the type is known, to make the most accurate decision.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405