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?

Community
  • 1
  • 1
bolov
  • 72,283
  • 15
  • 145
  • 224
  • For extra confusion, what if we give `B` a member of type `decltype(test());`? – aschepler Dec 10 '15 at 17:00
  • Pardon me but I don't see anything peculiar here at the point of instantiation (i.e., `foo()`) `B` and `A` are incomplete types. Thus, the compiler complains. – 101010 Dec 10 '15 at 17:33
  • 1
    @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. – bolov Dec 10 '15 at 17:34
  • blah trailing return types... – 101010 Dec 10 '15 at 17:37
  • 3
    There are two points of instantiations of `foo`, 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. – T.C. Dec 10 '15 at 17:56
  • @T.C. [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? – Columbo Jan 26 '16 at 13:30

0 Answers0