10

The question in the title is clear enough. To be more specific, consider the following example:

#include <type_traits>

template <typename T>
struct is_complete_helper {
    template <typename U>
    static auto test(U*)  -> std::integral_constant<bool, sizeof(U) == sizeof(U)>;
    static auto test(...) -> std::false_type;
    using type = decltype(test((T*)0));
};

template <typename T>
struct is_complete : is_complete_helper<T>::type {};

// The above is an implementation of is_complete from https://stackoverflow.com/a/21121104/5376789

template<class T> class X;

static_assert(!is_complete<X<char>>::type{}); 
// X<char> should be implicitly instantiated here, an incomplete type

template<class T> class X {};

static_assert(!is_complete<X<char>>::type{}); // #1

X<char> ch; // #2

This code compiles with GCC and Clang.

According to [temp.inst]/1:

Unless a class template specialization has been explicitly instantiated or explicitly specialized, 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.

X<char> is implicitly instantiated due to static_assert(!is_complete<X<char>>::type{}), which generates an incomplete type.

Then, after the definition of X, #1 suggests that X<char> is not instantiated again (still incomplete) while #2 suggests that X<char> is indeed instantiated again (becomes a complete type).

Is a specialization implicitly instantiated if it has already been implicitly instantiated? Why is there a difference between #1 and #2?

An interpretation from the standard is welcome.

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
xskxzr
  • 12,442
  • 12
  • 37
  • 77

1 Answers1

4

Is a specialization implicitly instantiated if it has already been implicitly instantiated?

No. According to [temp.point]/8:

A specialization for a class template has at most one point of instantiation within a translation unit.

x<char> need only be instantiated once, and it's not when it's named in the first static assertion, only before ch. But, [temp.point]/8 also says

A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. [...] If two different points of instantiation give a template specialization different meanings according to the one-definition rule, the program is ill-formed, no diagnostic required.

And is_complete_helper::test is a member function template whose declaration is instantiated before the static assertion. So it must also have an instantiation at the end of the TU. Where it will likely give a different result. So this trait is depending on an ill-formed NDR construct.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • So is it a bug that #2 compiles? – xskxzr Sep 06 '18 at 09:57
  • 1
    @xskxzr - The bug is in assuming `is_complete` can report different things. – StoryTeller - Unslander Monica Sep 06 '18 at 09:57
  • Oh I see the problem of `is_complete`. But why does `#2` compile? Isn't `X` always incomplete according to your interpretation? – xskxzr Sep 06 '18 at 10:04
  • @xskxzr - I see what you mean to ask. Just a sec. – StoryTeller - Unslander Monica Sep 06 '18 at 10:05
  • 1
    @xskxzr - Cleaned up the mess that was my ramble. Is it clear now? – StoryTeller - Unslander Monica Sep 06 '18 at 10:08
  • I have emphasized "... or when the completeness of the class type affects the semantics of the program". Doesn't the completeness of `X` affect the semantics of the program? – xskxzr Sep 06 '18 at 10:38
  • @Jarod42 - That applies to the template definition itself. Are you saying is_complete itself is ill-formed NDR? – StoryTeller - Unslander Monica Sep 06 '18 at 10:47
  • @xskxzr - No. The semantics of this single TU is to report false. It doesn't contradict the fact classes can be completed. It just means is_complete, as written cannot report the most up to date info, because it can only be instantiated once for *any type* in a single TU. – StoryTeller - Unslander Monica Sep 06 '18 at 10:50
  • I think so, in one case `test(X*)` doesn't "exists" in one place, and does in the other place. – Jarod42 Sep 06 '18 at 10:51
  • @StoryTeller I mean in the first `static_assert`, if `X` is complete, the `static_assert` fails, and if `X` is incomplete, the `static_assert` does not fail, so I conclude that the completeness of `X` affects the semantics of the program, thus `X` need to be implicitly instantiated in the first `static_assert`. What's wrong with this inference? – xskxzr Sep 06 '18 at 11:01
  • @xskxzr - The assumption that `is_complete` must adjust its report based on the completeness of its argument in a single TU. It doesn't. It has a single well-defined point of instantiation. It can only report what it was instantiated with *there*. – StoryTeller - Unslander Monica Sep 06 '18 at 11:21
  • @Jarod42 - The hypothetical instantiation of `is_complete` immediately after its definition cannot be with `X` (there is no `X` declared there). There really is no issue with the test, other than the fact it doesn't update itself when a type is completed in a TU. But that's intentional. – StoryTeller - Unslander Monica Sep 06 '18 at 11:23
  • @xskxzr - You must also remember that there is no looking into the future to know if the type will be instantiated later in the TU. There is one well-defined instantiation point for `is_complete>`, and that is before the static assertion. After which, it will not be instantiated again. I can see why this is unexpected (given the template's suggestive name), but I don't believe it's UB. – StoryTeller - Unslander Monica Sep 06 '18 at 11:26
  • @StoryTeller So doesn't the completeness of `X` *there* affect the semantics of the program? – xskxzr Sep 06 '18 at 11:29
  • @xskxzr - No, because `is_complete>` along with all of its members is only instantiated *once*, and never again in this TU. – StoryTeller - Unslander Monica Sep 06 '18 at 11:30
  • @StoryTeller Sorry I still don't get your point... As I said, "if `X` is complete, the `static_assert` fails, and if `X` is incomplete, the `static_assert` does not fail, so I conclude that the completeness of `X` affects the semantics of the program", all I said is about the first instantiation of `is_complete>`, I never mean `is_complete>` will be instantiated multiple times... – xskxzr Sep 06 '18 at 11:37
  • @xskxzr - Let's flip this around a bit. Is my last example ill-formed? – StoryTeller - Unslander Monica Sep 06 '18 at 11:38
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/179538/discussion-between-xskxzr-and-storyteller). – xskxzr Sep 06 '18 at 11:39
  • Wait... is `is_complete_helper::test` really instantiated since the substitution of types results in a deduction failure? – xskxzr Sep 06 '18 at 13:19
  • 1
    @xskxzr - There has to be an attempt to instantiate that declaration ([temp.inst/8](https://timsong-cpp.github.io/cppwp/n4659/temp.inst#8)) for substitution to fail. Though you raise a good point. Does it count for the purposes of [temp.point]/8? I think it should, but I find nothing conclusive. – StoryTeller - Unslander Monica Sep 06 '18 at 13:25