Template instantiation, two-phase name lookup, different behavior with automatic deduced type
Asked Answered
E

0

6

After seeing this question When is a C++ template instantiation type checked? , and wondering myself for quite some time the same thing I started to play with code to assimilate the knowledge. The answer gives clear and correct explanation. It mentions two-phase name lookup and the fact that the end of translation unit is also considered a point of instantiation for function templates. However I observed different behavior when using automatic return type deduction:

This is like the original code. It is correct and works as explained in the linked post:

class A;
class B;

template <class T> auto foo() -> T * {
  A *pa = nullptr; // ignore pa being `nullptr`.
  return static_cast<T *>(pa);
}

auto test() { return foo<B>(); }

class A {};
class B : public A {};

When using automatic return type deduction for the template foo, the definitions of A and B must appear before the instantiation point of foo:

Not working:

class A;
class B;

template <class T> auto foo()  { // automatic return type deduction
  A *pa = nullptr;
  return static_cast<T *>(pa);
}

auto test() { return foo<B>(); }

class A {};
class B : public A {};

The error by gcc (4.9 and 5.2.1):

error: invalid static_cast from type ‘A*’ to type ‘B*’

Clang gives a similar error

Working:

class A;
class B;

template <class T> auto foo()  { // automatic return type deduction
  A *pa = nullptr;
  return static_cast<T *>(pa);
}

class A {};
class B : public A {};

auto test() { return foo<B>(); }

Why it is happening? Why is that the rule about the end of compilation unit beeing considered a point of instantiation doesn't make the template instantiation legitimate anymore?

Ethel answered 10/12, 2015 at 16:52 Comment(6)
For extra confusion, what if we give B a member of type decltype(test());?Eyelid
Pardon me but I don't see anything peculiar here at the point of instantiation (i.e., foo<B>()) B and A are incomplete types. Thus, the compiler complains.Bionomics
@101010 But it works when foo has an explicit return type, even if A and B are incomplete types at the point if instantiation. See first example and the linked post.Ethel
blah trailing return types...Bionomics
There are two points of instantiations of foo<B>, one immediately after test, one at the end of the TU. The cast is invalid at the first point but not the second, so this looks like it's ill-formed NDR. The reason for the error showing up in only the deduced return type case is likely that the compiler needed to determine the return type of test (which isn't a template) and so it instantiated the body of foo at the first point rather than the second point.Cadge
@Cadge [temp.res]/8 does not apply, and the ODR does not consider the instantiated functions in both cases to be distinct. Why would it be ill-formed, NDR?Anonymous

© 2022 - 2024 — McMap. All rights reserved.