9

The following bit of code fails to compile on gcc 7.3.0 and clang 6.0.0 (but seems to compile fine under MSVC):

#include <utility>

struct IncompleteType;

template<typename T>
struct Container {
    T value;
};

using Uninstantiatable = Container<IncompleteType>;

auto foo() -> decltype(static_cast<Uninstantiatable const&>(std::declval<Uninstantiatable const&>())) {
    throw 1;
}

The error I get is this:

<source>:7:7: error: field has incomplete type 'IncompleteType'
    T value;
      ^
<source>:12:24: note: in instantiation of template class 'Container<IncompleteType>' requested here
auto foo() -> decltype(static_cast<Uninstantiatable const&>(std::declval<Uninstantiatable const&>())) {
                       ^
<source>:3:8: note: forward declaration of 'IncompleteType'
struct IncompleteType;
       ^
1 error generated.
Compiler returned: 1

Try it yourself here: https://godbolt.org/g/5AW37K

However, it compiles if I replace line 10 by

using Uninstantiatable = IncompleteType;

Like @Jarod42 mentioned, it compiles again if you remove the definition of Container: http://godbolt.org/g/ue9iwC It looks like gcc and clang therefore only instantiate the template class if it is defined.

In both cases, I'm simply trying to copy a const-ref to a const-ref, so I expect that to work regardless of what the type is, and this indeed works if the type itself is incomplete. Does the standard specify that a template instantiation is triggered here, or are gcc and clang incorrect in their behavior?

Note that the pattern in the code above is taken from the std::is_constructible implementation of gcc, and the error was triggered when I tried to copy a tuple containg a const ref of a templated class with an incomplete type parameter, so yes, this happens in practice.

Jarod42
  • 173,454
  • 13
  • 146
  • 250
Alex ten Brink
  • 879
  • 2
  • 8
  • 18
  • Correct me if I'm wrong, but this looks like a duplicate of https://stackoverflow.com/questions/8379002/does-casting-to-a-pointer-to-a-template-instantiate-that-template... – Max Langhof Jul 09 '18 at 13:03
  • 2
    Without definition of the template class, it [compiles](http://godbolt.org/g/ue9iwC) too. – Jarod42 Jul 09 '18 at 13:23

2 Answers2

1

According to the standard implicit class template instantiation should only be performed when a complete object is required [temp.inst]/1:

[...], the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program.

As pointed out in Jarod's comment, if a definition for container is not provided, Unintatiable is, independently of the completeness of InCompleteType, an incomplete type and the code compile. Moreover this static_cast is obviously independent of the completness of the object. So I think this is a compiler bug of gcc and clang.

Oliv
  • 16,492
  • 1
  • 24
  • 63
  • I'm not particularly sure, there are some cases when an instantiation is "allowed but not required". Not sure whether it applies here. https://timsong-cpp.github.io/cppwp/n4659/temp.inst#7 – llllllllll Jul 09 '18 at 16:06
0

If you don't instantiate your templated class, you can't get an error.

Having

 using Uninstantiatable = IncompleteType;

means that you generate only a reference on the IncompleteType. Having a reference or a pointer on undefined types is ok, as the compiler only has to genrate code for a pointer here. But if you instantiate your class:

template<typename T>
struct Container {
    T value;
};

Here you need the definition of T which is not a pointer or reference but the type itself which is here undefined. So the compiler can't generate a instance because it did not know anything about.

Having

decltype(static_cast<Uninstantiatable const&>) 

means that you get your template instantiated which results in the error. It has nothing todo with the point that you only need a reference in that statement. It has to do with generating the instance of the template itself which ca't be done because the "T" there is not known as described above.

Klaus
  • 20,019
  • 4
  • 46
  • 90
  • 2
    But if you remove `Container` definition, it [compiles](https://godbolt.org/g/ue9iwC) so why template is instantiated when definition is provided? – Jarod42 Jul 09 '18 at 13:09