22

TIL the following program is legal and whatnot:

#include <vector>

struct Bar;

struct Foo
{
    using BarVec = std::vector<Bar>::size_type;
};

struct Bar {};

int main()
{
   Foo f;
}

How? Bar is an incomplete type so the compiler has no way of knowing what std::vector<Bar> is, or that it contains a member size_type, or that the member size_type is a type.

The only explanation I can come up with is that any hypothetical specialisation would (presumably) have to already be in scope to cause size_type to take on a meaning different from that given in the "base" template definition, and size_type is not a dependent name (both factors contributing to the compiler's certainty).

What's the legal rationale here?

Lightness Races in Orbit
  • 358,771
  • 68
  • 593
  • 989
  • None of these directly (i.e. pointing to a specific section of the standard) answer your question, but they provide some insight [here](https://stackoverflow.com/questions/8329826/can-standard-container-templates-be-instantiated-with-incomplete-types), [here](https://stackoverflow.com/questions/620378/why-do-c-templates-let-me-circumvent-incomplete-types-forward-declarations), and [here](https://stackoverflow.com/questions/22770318/incomplete-types-in-template-code) – Cory Kramer Jul 10 '15 at 16:05
  • 1
    How did you learn that the program is legal and whatnot? I was able to compile it, but if you have a more authoritative source we may be able to work from there. – Drew Dormann Jul 10 '15 at 16:19
  • 1
    @DrewDormann: Well, to be fair, nothing authoritive (that's what I'm looking for here), but [nobody here seems to have any problems or complaints](http://stackoverflow.com/q/31342839/560648). "Actually it's _not_ legal" with proof would be a perfectly valid answer here. :) – Lightness Races in Orbit Jul 10 '15 at 16:19
  • @DrewDormann this comes from my answer [here](http://stackoverflow.com/a/31343136/4342498) – NathanOliver Jul 10 '15 at 16:20
  • According to [this](http://en.cppreference.com/w/cpp/language/template_parameters), it's legal. – nasser-sh Jul 10 '15 at 16:21
  • 1
    The legal rationale is that this is UB by [res.on.functions]/2.5 pre-C++1z: "In particular, the effects are undefined in the following cases: [...] if an incomplete type (3.9) is used as a template argument when instantiating a template component, unless specifically allowed for that component." – T.C. Jul 10 '15 at 17:53
  • 1
    In the more general case, the answer is that incompleteness doesn't matter; `Bar` is still just a single type, not a bazillion possible types. `void` is an incomplete type that cannot be completed; we instantiate templates over `void` all the time. The "later specialization" scenario is outlawed by assorted rules in the standard. – T.C. Jul 10 '15 at 17:57
  • I didn't want to dominate the comments section here, so I gave this a few hours to simmer. I believe that a *hypothetical specialisation* is irrelevant here, as it's [UB *not* to define that specialization before it would be used](http://stackoverflow.com/questions/5912689). I think that reduces the question to "can `using BarVec = std::vector::size_type;` be well-defined for an incomplete type`X`?" I could *swear* that one of our regular, standard-quoting C++ hotshots answered that in the affirmative a few months ago, for type definitions that didn't require the definition of `X`. – Drew Dormann Jul 10 '15 at 18:43
  • I'm curious, would you consider a simpler example equivalent: ``template struct Foo { void qux(T t) { t.qux() } }; using baz = Foo`` where Bar is incomplete as before. It seems like this should not compile, but it does. – Nir Friedman Jul 12 '15 at 18:59
  • @NirFriedman: Dunno, fix the three syntax errors then maybe :) – Lightness Races in Orbit Jul 13 '15 at 03:28

1 Answers1

14

I think in practice this may work but from what I can tell this looks like undefined behavior. From the draft C++11 standard 17.6.4.8 [res.on.functions]:

In particular, the effects are undefined in the following cases:

[...]

  • if an incomplete type (3.9) is used as a template argument when instantiating a template component, unless specifically allowed for that component.

Although instantiating a template component does not seem like a well-defined term.

I came to this via LWG defect 611 which added:

unless specifically allowed for the component.

to the end of the bullet above so it now reads:

if an incomplete type (3.9) is used as a template argument when instantiating a template component, unless specifically allowed for the component.

as an exception for shared_ptr since the above quote conflicted with this quote from 20.6.6.2 [util.smartptr.shared]:

The template parameter T of shared_ptr may be an incomplete type.

Also see N4371: Minimal incomplete type support for standard containers, revision 2.

Shafik Yaghmour
  • 143,425
  • 33
  • 399
  • 682