From: Johannes Schaub (litb) on
Andy Venikov wrote:

> Alf P. Steinbach wrote:
>> * Andy Venikov:
>>> I remember there have been quite a few discussions on this topic
>>> and why
>>>
>>> class A
>>> {
>>> vector<A> vec_;
>>> };
>>>
>>> is actually well-formed under C++98,
>>
>> Assuming that 'vector' is 'std::vector' it's not well-formed (see below).
>>
>>
>>> but I can't seem to find any of the threads.
>>>
>>> I wanted to revisit all the reasoning behind it.
>>>
>>> Could someone refresh my memory as to what it's
>>> well-formed or point to one of those all threads?
>>
>> A type T is "incomplete" at a point where you can't do sizeof(T),
>> otherwise it's
>> complete; the element type of a standard library container must be
>> complete.
>>
>> So, for a standard library container type C, if you can do sizeof(E)
>> where E is
>> the container element type, then at this point you use C<E>, otherwise
>> not.
>
> I think it's not the whole story.
> If you follow this logic, then it'd be impossible to do Curiously
> Recurring Template Pattern as sizeof(Derived) isn't known when you
> have to instantiate Base<Derived>.
>

From a core language point of view, a template type argument can be an
incomplete type very well (like "void"). So your observation about CRTP
doesn't mean anything wrt std::vector. You can't instantiate a standard
library "template component" using such an incomplete type.

Notice that it makes a difference if you declare

struct A {
static vector<A> a_; // fine
};

vector<A> A::a_;

That's fine, because vector<A> is not instantiated at "// fine".

> Now it slowly comes back to me. The trick I think is the two-phase
> lookup. If you implement vector in such a way that all your
> non-dependent manipulations don't need to know the size of the element,
> then by the time second phase kicks in, the size of the element is
> known. That's why, I think, it all depends on how vector has been
> implemented and not strictly on the language rules.
>

For a class template that's implicitly instantiated, only member
declarations but not their definitions are implicitly instantiated. If in
their declaration-only part appears no "sizeof(T)" or a concept check for
completeness of T or something, it may go unnoticed.

So i think i have to correct myself wrt whether the code you show is well-
formed or not: It depends on what std::vector does. But it certainly
contains undefined behavior. If it takes the sizeof of T, then that would
make the program ill-formed (however since the program also contained
undefined behavior, no diagnostic is required as said by 1.4/2 last bullet).

> BTW, comeau is perfectly happy with the code snipped I gave at the
> beginning of this post.
>
That's just showing it doesn't do sanity checks (with your config, at
least).

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Alf P. Steinbach on
* Nick Hounsome:
> On 9 Mar, 05:20, "Alf P. Steinbach" <al...(a)start.no> wrote:
>> * Andy Venikov:
>>
>>> I remember there have been quite a few discussions on this topic
>>> and why
>>> class A
>>> {
>>> vector<A> vec_;
>>> };
>>> is actually well-formed under C++98,
>> Assuming that 'vector' is 'std::vector' it's not well-formed (see below).
>>
>>> but I can't seem to find any of the threads.
>>> I wanted to revisit all the reasoning behind it.
>>> Could someone refresh my memory as to what it's
>>> well-formed or point to one of those all threads?
>> A type T is "incomplete" at a point where you can't do sizeof(T), otherwise it's
>> complete; the element type of a standard library container must be complete.
>>
>> So, for a standard library container type C, if you can do sizeof(E) where E is
>> the container element type, then at this point you use C<E>, otherwise not.
>
> It is OK because sizeof(A.vec_) is not dependent on sizeof(A)

Sorry, no, it's not OK. Your point is relevant for most (all?) in-practice
std::vector implementations, but here you're up against formally Undefined
Behavior. And that means that in spite of practical considerations some
implementation might just not work or compile; it might, to ensure that you're
writing Good Code, even check explicitly that type A is complete.


> This is just a tarted up way of saying something like:
>
> struct A
> {
> struct A* vec_;
> };

Sorry, no, that depends on the implementation; see above.


Cheers & hth.,

- Alf

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Martin B. on
Alf P. Steinbach wrote:
> * Nick Hounsome:
>> On 9 Mar, 05:20, "Alf P. Steinbach" <al...(a)start.no> wrote:
>>> * Andy Venikov:
>>>
>>>> I remember there have been quite a few discussions on this topic
>>>> and why
>>>> class A
>>>> {
>>>> vector<A> vec_;
>>>> };
>>>> is actually well-formed under C++98,
>>> Assuming that 'vector' is 'std::vector' it's not well-formed (see
>>> below).
>>>
>>>> but I can't seem to find any of the threads.
>>>> I wanted to revisit all the reasoning behind it.
>>>> Could someone refresh my memory as to what it's
>>>> well-formed or point to one of those all threads?
>>> A type T is "incomplete" at a point where you can't do sizeof(T),
>>> otherwise it's
>>> complete; the element type of a standard library container must be
>>> complete.
>>>
>>> So, for a standard library container type C, if you can do sizeof(E)
>>> where E is
>>> the container element type, then at this point you use C<E>,
>>> otherwise not.
>>
>> It is OK because sizeof(A.vec_) is not dependent on sizeof(A)
>
> Sorry, no, it's not OK. Your point is relevant for most (all?) in-practice
> std::vector implementations, but here you're up against formally Undefined
> Behavior. And that means that in spite of practical considerations some
> implementation might just not work or compile; it might, to ensure that
> you're
> writing Good Code, even check explicitly that type A is complete.
>

Do you (or someone else) know if an implementation of std::vector that
compiles without errors for an incomplete type could still break at runtime?

cheers,
Martin

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Randy on
On Mar 9, 8:19 am, Kris Prad <krisp...(a)yahoo.co.uk> wrote:

[snip]

> Comeau accepts even this:
>
> #include <vector>
>
> struct A
> {
> std::vector<A> vec_;
> A(): vec_(10) {} // <----- with size
>
> };
>
> BUT NOT THIS:
>
> struct B;
> std::vector<B> vec2_; // incomplete type error
>
> Kris
>

Actually this example appears, IMHO, to be correct based on my reading
of the Standard, 9.2, Class Members, paragraph 2, which states that a
class is considered as a complete type at several points in the class
member specification, explicitly including in ctor initializer lists,
as shown above.

Randy.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Seungbeom Kim on
Randy wrote:
> On Mar 9, 8:19 am, Kris Prad <krisp...(a)yahoo.co.uk> wrote:
>> Comeau accepts even this:
>>
>> #include <vector>
>>
>> struct A
>> {
>> std::vector<A> vec_;
>> A(): vec_(10) {} // <----- with size
>>
>> };
>>
>> BUT NOT THIS:
>>
>> struct B;
>> std::vector<B> vec2_; // incomplete type error
>
> Actually this example appears, IMHO, to be correct based on my reading
> of the Standard, 9.2, Class Members, paragraph 2, which states that a
> class is considered as a complete type at several points in the class
> member specification, explicitly including in ctor initializer lists,
> as shown above.

A::vec_ would be considered as a complete type in the initializer list
of A::A(), of course. The issue here is (kind of) the other direction,
i.e. whether A would be complete in the instantiation of std::vector<A>
which is the type of A::vec_.

--
Seungbeom Kim

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]