Conversion constructor for fundamental types when allocating from the heap using new

Please see the discussion between Sara and I here: http://www.experts-exchange.com/Q_27382621.html

Clearly, we disagree over our interpretation of the C++ standard so I thought this might be a nice one for a discussion topic. So the question is:

From a constructor conversion point of view, is this...

size_t s = 1000;
char c = char(s);

...semantically the same as this...


size_t s = 1000;
char * pc = new char(s);

...in the sense that in the latter case a constructor conversion take please just like it does in the former cast.

The C++ standard states...

A simple-type-specifier followed by a parenthesized expression-list constructs a value of the specified
type given the expression list. If the expression list is a single expression, the type conversion expression
is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4).

5.4 Explicit type conversion (cast notation) [expr.cast]
1 The result of the expression (T) cast-expression is of type T. The result is an lvalue if T is a reference
type, otherwise the result is an rvalue. [Note: if T is a non-class type that is cv-qualified, the cv-qualifiers
are ignored when determining the type of the resulting rvalue; see 3.10. ]

And as well as the links I posted in the other thread, in support of my understanding I also offer...

http://bit.ly/pOeD2b and http://bit.ly/nPUf1I

The main problem is I was unable to locate anything that specifically stated or was unequivocally clear about the fact that when using the new operator to create a fundamental type if a value is passed into the constructor of said type it will perform a constructor conversion cast just the same as if the fundamental was being created on the stack.

Points will be awarded for anyone who can provide absolute proof one way or the other. I don't mind being wrong (although I'm pretty sure I am now) but given there are two differing views on this and I was unable to provide a single and unequivocal and authoritative example, for Sara, in the case of fundamental types.

I look forward to your thoughts.


LVL 40
evilrixSenior Software Engineer (Avast)Asked:
Who is Participating?
 
Infinity08Commented:
In 5.3.4, the standard says (among other things) this about the initializer in a new expression :

        — If the new-initializer is of the form (expression-list) and T is a class type, the appropriate constructor is
        called, using expression-list as the arguments (8.5);
        — If the new-initializer is of the form (expression-list) and T is an arithmetic, enumeration, pointer, or
        pointer-to-member type and expression-list comprises exactly one expression, then the object is initial-
        ized to the (possibly converted) value of the expression (8.5);

So, in the normal case, the appropriate constructor is called, but in a few special cases (like in the case presented here), the handling is slightly different. The initializer value (s) is converted to the target type (char) first, and then the char is initialized with this converted value.

The conversion that happens goes like this (from 8.5) :

        The initialization that occurs in new expressions (5.3.4), static_cast expressions (5.2.9), functional
        notation type conversions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization
        and is equivalent to the form
            T x(a);

        <SNIP>

        — Otherwise, the initial value of the object being initialized is the (possibly converted) value of the initial-
        izer expression. Standard conversions (clause 4) will be used, if necessary, to convert the initializer
        expression to the cv-unqualified version of the destination type; no user-defined conversions are consid-
        ered. If the conversion cannot be done, the initialization is ill-formed.

And the standard conversions mentioned above, are described like this (from 4.7) :

        If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source
        integer (modulo 2 n where n is the number of bits used to represent the unsigned type). [Note: In a two’s
        complement representation, this conversion is conceptual and there is no change in the bit pattern (if there
        is no truncation). ]
        If the destination type is signed, the value is unchanged if it can be represented in the destination type (and
        bit-field width); otherwise, the value is implementation-defined.

Now, here comes the tricky bit : you'll see that the behavior depends on the signed-ness of the target type, and since the signed-ness of the char type is implementation defined, the behavior of the presented case is also implementation defined - the standard doesn't say what value *pc will have. If s would have been <= CHAR_MAX though, things would have been nicely defined.
0
 
sarabandeCommented:
the problem i have with the view of evilrix is that any valid "cast" wouldn't generate a warning, even for obviously bad casts. for example

double d = -12345.6789;
unsigned char * c = new unsigned char(d);

Open in new window


the vc compilers i have installed all would give a cast warning (i don't know why evilrix only can reproduce the warning when setting to 64-bit compatibility, but anyway).

i could suppress the warning by using an additional cast.

size_t s = str.length();
char * pclen = new char(char(s));

Open in new window


what seems fair to me.

also note when using new with class type we don't have a cast but a normal construction.

Sara

0
 
rbrideCommented:
Maybe I don't understand the question. But the first part
size_t s = 1000;
char c = char(s);

Open in new window


converts 1000 to a character which will disappear when it goes out of scope.

The second part
size_t s = 1000;
char * pc = new char(s);

Open in new window

converts 1000 to a character and leaves it on the heap to be explicity deleted when you are finished.

An array of characters needs to be created with the square brackets operator [] or use the string classes from the standard library.
0
Cloud Class® Course: Python 3 Fundamentals

This course will teach participants about installing and configuring Python, syntax, importing, statements, types, strings, booleans, files, lists, tuples, comprehensions, functions, and classes.

 
sarabandeCommented:
rbridge, we discussed whether the char(s) should give a cast warning like "conversion from 'size_t' to 'char', possible loss of data" or not.

at my environments the first sample doen't give that warning cause it was looked on as a cast while the second does. evilrix says the second with new operator aslo is a cast and should not give warning.

Sara

0
 
evilrixSenior Software Engineer (Avast)Author Commented:
The resultant value is implementation defined but a conversion still takes place, right? That is how I read and have always understood it.

Don't you just love the wording of the standard somerimes? ;)
0
 
Infinity08Commented:
Yes, conversion takes place.
0
 
evilrixSenior Software Engineer (Avast)Author Commented:
>> Yes, conversion takes place.
Thanks, that was the crux :)
0
 
sarabandeCommented:
it never was a disagreement about 'conversion'.  

with the following code there is also a conversion though it would generate a warning.

size_t s = 10;
char c = s;

Open in new window

  // warning, possible loss of data

the question was whether the expression right of new was an explicit cast or not. if not a warning is appropriate.

Sara
0
 
sarabandeCommented:
you might check our previous thread where i asked 'evilrix, where do you see a cast?'.

you told that the expression 'new char(s)' would be a cast operation while from standard it is clear that it is an initialisation as Infinity08 showed.

Sara


0
 
Infinity08Commented:
The point is, that if there is a valid conversion between the source type (size_t) and the target type (char), it will be done before the initialization. ie. it's an implicit conversion, that I wouldn't expect to generate a warning.

Such a conversion between size_t and char is valid, as the quote from the standard above explains, albeit with the mentioned caveats (of loss of precision and/or implementation defined behavior).

Calling such a conversion a cast is not 100% accurate (because a cast has a slightly different meaning in standardese), but the underlying idea that the expression is valid, is correct.

If your compiler generates a warning for this case, then that's ok too. It doesn't have to (because there's nothing wrong with the code), but it can decide to do so to point out a loss of precision and/or implementation defined behavior. In many cases, having such a warning would be useful, if only to catch unintentional mistakes by the programmer. In other cases, such a warning can be annoying, in the case where the programmer really intended to write the code that way.
0
 
evilrixSenior Software Engineer (Avast)Author Commented:
>> you told that the expression 'new char(s)' would be a cast operation while from standard it is clear that it is an initialisation as Infinity08 showed

The standard also states:

5.2.3 Explicit type conversion (functional notation) [expr.type.conv]
1 A simple-type-specifier (7.1.5) followed by a parenthesized expression-list constructs a value of the specified
type given the expression list. If the expression list is a single expression, the type conversion expression
is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4).
If
the simple-type-specifier specifies a class type, the class type shall be complete. If the expression list specifies
more than a single value, the type shall be a class with a suitably declared constructor (8.5, 12.1), and
the expression T(x1, x2, ...) is equivalent in effect to the declaration T t(x1, x2, ...); for
some invented temporary variable t, with the result being the value of t as an rvalue.

5.4 Explicit type conversion (cast notation) [expr.cast]
1 The result of the expression (T) cast-expression is of type T. The result is an lvalue if T is a reference
type, otherwise the result is an rvalue. [Note: if T is a non-class type that is cv-qualified, the cv-qualifiers
are ignored when determining the type of the resulting rvalue; see 3.10. ]

Note that both are called "Explicit type conversion", which is what a cast is. If you want to be picky only one of those is 'cast notation' but both are an explicit type conversion and the standard makes it clear they are the same thing. I posted this pretty early on in the thread. I also, on plenty of occasions, referred to it as a conversion. I also explained a number of types why I referred to it as a cast/conversion and also what was happened (although not in as much technical detail as Infinity08 -- thanks for that) so I don't really see there was any ambiguity.

>> with the following code there is also a conversion
Indeed, but that is an "implicit conversion".

4 Standard conversions

Standard conversions are implicit conversions defined for built-in types. Clause 4 enumerates the full set of
such conversions. A standard conversion sequence is a sequence of standard conversions in the following
order:
— Zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion,
and function-to-pointer conversion.
— Zero or one conversion from the following set: integral promotions, floating point promotion, integral
conversions, floating point conversions, floating-integral conversions, pointer conversions, pointer to
member conversions, and boolean conversions.
— Zero or one qualification conversion.

You still don't have to agree nor believe this is actually a cast but I just hope my reasoning is now clear.


0
 
evilrixSenior Software Engineer (Avast)Author Commented:
BTW: Infinity08, whilst I am happy you've posted the correct answer I'd like to keep this open for a few days just in case anyone has anything else they'd like to offer as a for or against the argument :)
0
 
sarabandeCommented:
Infinity08, you posted from C++ standard that the expression in question, means the 'new char(s)' is formally equivalent to an expression 'T x(a)'.

the second never could be called an explicit cast but it is always implicit, right?

an explicit cast would be in  'T x = (T)a;'  or in 'T * pc = new T((T)a)'.

note, i don't mind if you - now - say that your argumentation always included implicit casting though i never disagreed on that. but you should admit that in the expression 'new char(s)' the char(s) is not a rvalue and not a single expression as required to apply 5.2.3. if you don't agree try to compile the following:

char* p = new (char(10));

Open in new window



Sara
0
 
sarabandeCommented:
the last part is directed to evilrix and not to infinity08. sorry.

Sara
0
 
Infinity08Commented:
>> the second never could be called an explicit cast but it is always implicit, right?

What happens in the presented code, is an implicit conversion from size_t to char, before initializing the char with this converted value. The conversion does happen though, and thus there's technically no reason for a warning, or no need for an extra explicit cast.
0
 
sarabandeCommented:
infinity08, thank you.

the warning i got on vc was only the trigger not the reason for me to insisting that in the

char* p = new char(s);

Open in new window



no explicit cast was done but that it was equivalent to an initialisation like  

char c = s;

Open in new window



i also compiled on linux, solaris and aix and didn't get a warning what is ok (though i prefer the vc behavior).

Sara
0
 
evilrixSenior Software Engineer (Avast)Author Commented:
Sorry, what are you asking me to admit to? I don't think there is anything I need to admit to as all I've done is quote from the standard and explained my interpretation of it, something that Infinity08 seems to agree with although, as I've already "admitted", he's done a lot better job of interpreting it for us.

If you still disagree, that's fine but you're not going to put words into my mouth!?
0
 
sarabandeCommented:
??

>> i also am not convinced that the new char(s) actually is a cast
You don't have to be convinced -- doesn't change the fact that it is exactly what it is.

Quoted from the C++03 standard...

A simple-type-specifier followed by a parenthesized expression-list constructs a value of the specified
type given the expression list. If the expression list is a single expression, the type conversion expression
is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4).

5.4 Explicit type conversion (cast notation) [expr.cast]
1 The result of the expression (T) cast-expression is of type T. The result is an lvalue if T is a reference
type, otherwise the result is an rvalue. [Note: if T is a non-class type that is cv-qualified, the cv-qualifiers
are ignored when determining the type of the resulting rvalue; see 3.10. ]

with the above you made the statement that the expression 'new char(s)' would perform an explicit cast. that is not true.

the 'new char(s)' makes an implicit cast from size_t to char and then initialises the newly created char with the casted value.

you will find more samples of that assertment, even in the initial question:

size_t s = 1000;
char * pc = new char(s);

...in the sense that in the latter case a constructor conversion take please just like it does in the former cast.

Sara

 
0
 
evilrixSenior Software Engineer (Avast)Author Commented:
Sara, you are now just picking on pointless semantics, none of which are really relevant to the fact that the compiler is performing an conversion that has been explicitly coded by the programmer.

One final time...

"If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4)."

" The result of the expression (T) cast-expression is of type T."

--If the expression list is a single expression

new char(T) // this is a single expression list simple-type-specifier that just happens to have its storage allocated on the heap rather than the stack.

" If the new-initializer is of the form (expression-list) and T is an arithmetic, enumeration, pointer, or
        pointer-to-member type and expression-list comprises exactly one expression, then the object is initial-
        ized to the (possibly converted) value of the expression"

All that states is it is "converted".

Now if we consider that those various sections from the standard are all referring to function style conversion and the standard defines a functional style conversion and being explicit (5.2.3 Explicit type conversion (functional notation) ) it would seem to me that it is not unreasonable to infer that it can be considered an explicit cast.

That all said, none of this is relevant since all you are doing is shifting the focus onto pointless semantics to avoid recognising that there is, indeed, a[n] cast/implicit/explicit [whatever the hell you want to call it]  type conversion going on here. For that reason I stated in the original question that the code was correct in so far as it is well formed and, thus, whilst it was unlikely the original programmer mean char(N) instead of char[N] we couldn't preclude the fact that that might be exactly what they meant to do.

Is it really to hard for you to just to accept that and acknowledge that my original assertion came from somewhere other than thin air -- ie, I was basing in on my interpretation of C++03, and that as far as it goes my interpretation isn't really wrong.


0
 
evilrixSenior Software Engineer (Avast)Author Commented:
Thanks Infinity08 for providing a clearer interpretation of the standard.
0
 
Hugh McCurdyCommented:
Even though I don't know enough C++ to participate, this was educational.  Thanks.
0
 
evilrixSenior Software Engineer (Avast)Author Commented:
Heh. Welcome, I'm pleased someone found it useful :)
0
 
sarabandeCommented:
you opened that thread to clear a difference between us.

what sense does the clarification of infinity08 make if you say it was the same you said?

"quote from standard posted by infinity08"

If the new-initializer is of the form (expression-list) and T is an arithmetic, enumeration, pointer, or pointer-to-member type and expression-list comprises exactly one expression, then the object is initialized to the (possibly converted) value of the expression (8.5);

i understand that there was an conversion of the argument and then an initialization.

you may answer yourself who of us was nearer to that when rereading the threads.

thank all for the discussion. i now will close.

Sara

0
 
evilrixSenior Software Engineer (Avast)Author Commented:
>> i understand that there was an conversion of the argument and then an initialization.
Of course, Sara, it was you. Congratulations on winning an argument by deflection and by wearing the other party down to the point there they just don't care any more and have actually lost the will to live.
0
 
sarabandeCommented:
>> then the object is initialized to the (possibly converted) value

>> there was a conversion of the argument and then an initialization

it is the last i want, to wear anyone down, especially regarding a point where the knowledge of the English language and the knowledge of the C++ standard is asked for which both do not belong to my strengths.

but i can't say other that for me both assertions tell the same. they also in my opinion support what i told a multiple time like in

>> i think the (s) is an initializer and the char(s) works like a default copy constructor for the char type.

or in

>> indicates they create (construct) one single char and use the value given in the parantheses for initializing purpose.

sorry, that you lost the will to live. but as you possibly could be my son (i am born in 1949) i am sure you will live some decades longer :)

Sara


0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.