?
Solved

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

Posted on 2011-10-11
25
Medium Priority
?
408 Views
Last Modified: 2012-05-12
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.


0
Comment
Question by:evilrix
  • 10
  • 9
  • 4
  • +2
25 Comments
 
LVL 35

Expert Comment

by:sarabande
ID: 36947237
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
 
LVL 4

Expert Comment

by:rbride
ID: 36947252
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
 
LVL 35

Expert Comment

by:sarabande
ID: 36947275
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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 53

Accepted Solution

by:
Infinity08 earned 2000 total points
ID: 36947524
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
 
LVL 40

Author Comment

by:evilrix
ID: 36947602
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
 
LVL 53

Expert Comment

by:Infinity08
ID: 36947616
Yes, conversion takes place.
0
 
LVL 40

Author Comment

by:evilrix
ID: 36947623
>> Yes, conversion takes place.
Thanks, that was the crux :)
0
 
LVL 35

Expert Comment

by:sarabande
ID: 36947893
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
 
LVL 35

Expert Comment

by:sarabande
ID: 36948004
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
 
LVL 53

Assisted Solution

by:Infinity08
Infinity08 earned 2000 total points
ID: 36948233
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
 
LVL 40

Author Comment

by:evilrix
ID: 36948277
>> 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
 
LVL 40

Author Comment

by:evilrix
ID: 36948290
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
 
LVL 35

Expert Comment

by:sarabande
ID: 36948658
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
 
LVL 35

Expert Comment

by:sarabande
ID: 36948674
the last part is directed to evilrix and not to infinity08. sorry.

Sara
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 36948704
>> 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
 
LVL 35

Expert Comment

by:sarabande
ID: 36949267
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
 
LVL 40

Author Comment

by:evilrix
ID: 36949714
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
 
LVL 35

Expert Comment

by:sarabande
ID: 36950084
??

>> 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
 
LVL 40

Author Comment

by:evilrix
ID: 36950303
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
 
LVL 40

Author Closing Comment

by:evilrix
ID: 36950309
Thanks Infinity08 for providing a clearer interpretation of the standard.
0
 
LVL 13

Expert Comment

by:Hugh McCurdy
ID: 36950396
Even though I don't know enough C++ to participate, this was educational.  Thanks.
0
 
LVL 40

Author Comment

by:evilrix
ID: 36950402
Heh. Welcome, I'm pleased someone found it useful :)
0
 
LVL 35

Expert Comment

by:sarabande
ID: 36950415
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
 
LVL 40

Author Comment

by:evilrix
ID: 36950477
>> 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
 
LVL 35

Expert Comment

by:sarabande
ID: 36950872
>> 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

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Article by: SunnyDark
This article's goal is to present you with an easy to use XML wrapper for C++ and also present some interesting techniques that you might use with MS C++. The reason I built this class is to ease the pain of using XML files with C++, since there is…
This article will show you some of the more useful Standard Template Library (STL) algorithms through the use of working examples.  You will learn about how these algorithms fit into the STL architecture, how they work with STL containers, and why t…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
Suggested Courses

616 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question