[cpp-threads] More niggles on the strawman proposal

Nick Maclaren nmm1 at cus.cam.ac.uk
Thu Jan 19 11:09:09 GMT 2006


Ben Hutchings <ben at decadentplace.org.uk> wrote:
>
> > Upon studying it, C++ seems to have eliminated (or not introduced) all
> > of the worst problems.  My reading of C++ is that basic actions take
> > place on most derived types,
> 
> What do you mean by "basic actions"?

The actions that are the concern of the memory model - i.e. the units of
'overlapping'.  For example, initialising an integer array is made up of
the basic actions of initialising each element, but (simple) copying of
a structure is a basic action.

> > and that most (or all) of the ghastliest
> > punning allowed by C is forbidden in C++.
> 
> Most of the ghastliest type-punning in C is strictly forbidden by the C
> standard too!

I am afraid that it is not like that - see my Objects diatribe.  The
problem is that what is forbidden, what is explicitly permitted and what
is implicitly permitted all overlap, and there is no specification of
which overrides the other.  This means that there are numerous
incompatible interpretations.

It isn't possible to take the extreme positions, either.  Regarding
all specification of undefined behaviour as overriding those of defined
behaviour means that ALL C programs have undefined behaviour, so the C
standard specifies NOTHING about run-time behaviour.  And allowing
anything that is defined to override undefined behaviour leads to
inconsistency :-(

> C++ has stricter rules for non-POD types, but for POD
> types I'm not aware of any restrictions on type-punning beyond those in
> C.  There's actually a slight relaxation in that an object of POD type
> may also be accessed as an array of char or unsigned char, where C
> doesn't promise this for an array of char.

Absolutely not.  It is an order of magnitude tighter.  Bjarne deliberately
excluded some of the less safe features of C - see C.3.9 for one - but
most of them are not actually mentioned in Annex C.  See also 1.8, 3.9
and 3.10 para. 15 for something that is a LOT tighter than C.

> > There are only three issues that I can't resolve.
> > 
> > The first is that I can't find where it specifies when the array to
> > pointer conversion is performed, and when it is not.
> 
> It is done wherever an rvalue is required.

Yes, but where is that specified?  It makes an important difference, in
that it could affect the overlap rules for array-valued built-in
library functions.

> The conversion itself is specified as a standard conversion in
> [conv.array] (4.2).  There are references in several places to
> lvalue-to-rvalue conversion, array-to-pointer conversion and
> function-to-pointer conversion, the latter two covering the types that
> don't have rvalues.

Yes, but I haven't found anything that says WHERE it is done :-(

> C++ makes the same adjustment of array-typed parameters as C, so for
> example the declarations
> 
>     void f(const char []);
>     void f(const char *);
> 
> refer to the same function.

That helps.

> It is possible to pass array lvalues as references, but then they must
> be of known dimension, e.g.:
> 
>     void f(const char (&)[10]);
> 
> Unfortunately the overload rules are such that f("abcdefghi") would be
> ambiguous; both these functions are an equally good match for an
> argument of type const char[10].

That doesn't :-(

> > This isn't introduced by threading, of course, because it also shows up
> > in "(a[1] = 0)+(b = 1)", but it will be more important in threaded
> > programs.  It needs clarifying, unless I have simply failed to find the
> > reference.
> 
> The specification of unions in [class.union] (9.5/1) says that only one
> member can hold a value at once.  So even though a[1] does not overlap
> b, it is undefined behaviour to read it after writing to b.  So I think
> the above multithreaded code will be defined to have a data race without
> any need for particular mention of unions.

Not at all.  Consider:

    typedef union (int a[2]; int b;} z;
    z y;
    int *p = (int *)&z+1;

Thread 1:  *p = 0;
           now use *p;
Thread 2;  z.b = 1;

C++ makes it very clear that the intent of the standard is that sort of
dirty trick is undefined behaviour; C both implies that and implies the
converse.  Hence I don't think that it is a major problem for C++ the
way it is for C.

> >     1) C++ makes a definition visible before it is initialised (3.3.1)
> > and forbids access to its members only for const-qualified objects
> > (8.5).  In fact, there is an implication that constructors with
> > side-effects are called sequentially in sub-object order.
> 
> That's guaranteed by [class.base.init] (12.6.2).

I can't find that.  Can you give a paragraph number?

> Initialisation order is sort of implied by the wording in 8.5.1/10, but
> that's rather tenuous.  Arrays cannot be assigned to, but they can be
> copied by a default copy-constructor of a class type (12.8/8), and that
> certainly doesn't specify the order in which array elements are copied.

I can read the wording either way :-(  This will need clarification.


Regards,
Nick Maclaren,
University of Cambridge Computing Service,
New Museums Site, Pembroke Street, Cambridge CB2 3QH, England.
Email:  nmm1 at cam.ac.uk
Tel.:  +44 1223 334761    Fax:  +44 1223 334679



More information about the cpp-threads mailing list