Journal Articles
Browse in : |
All
> Journals
> CVu
> 114
(20)
All > Journal Columns > LettersEditor (132) Any of these categories - All of these categories |
Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.
Title: Questions & Answers
Author: Administrator
Date: 03 June 1999 13:15:31 +01:00 or Thu, 03 June 1999 13:15:31 +01:00
Summary:
Body:
Question from: Nick Wedd <Nick@maproom.co.uk>
Dear Francis,
I am writing about your article in the April issue of EXE.
Usually, I find your articles interesting, and I am able to understand them. Rarely, they are of no interest to me and I do not try to understand them. But while this one is definitely interesting to me and relevant to my work, I have still failed to understand it even at a third reading.
You say "the STL is value-based". What does this mean? What would the alternative be?
You say "Our card class cannot be used in an STL collection". You do not say what would happen if we tried to use it in one.
In my code I define my own classes, and use them in the STL without problems. If I define a class wombat, I find I can have a list or an array of wombats, or of wombat*s. I suppose I could have a list of wombat&s, but I still don't feel comfortable with references.
Of course, whenever I define a class, I write a destructor, a copy-constructor, and an assignment operator. I feel it would be asking for trouble to leave them out. I guess that if did leave them out, all sorts of things would not work: I think I would not be able to pass them as arguments and use them as return values.
In the sample code at the bottom right of page 47 (see sidebar), I cannot understand how the line:
answer = Metre(len1-len2);
can work. Metre::operator- has not been defined. Does the compiler somehow decide that it had better cast the Metres to doubles before doing arithmetic on them? If Metre::int() had been defined as well, how would it know which cast to use?
I would be grateful if you could help me to understand this.
This letter raises several interesting points as well as demonstrating something often missed by technical writers, even intelligent and diligent readers can face problems if they lack the assumed foundation knowledge.
I have a rigid definition of a pure value type - it has a public copy constructor and copy assignment operator (these can be either implicit or explicit). On the other hand a pure object type for me is one that has neither of these as public properties. A pure object has a location (a fixed address) and can only be passed by reference or accessed through a pointer.
Now all the facilities (algorithms and member functions of the template containers) provided by the STL function by copying items in the containers. In other words the items must be copyable - in my terms they must be values because they must be movable.
We could have had collections that held items via references had C++ references been like Java ones (reseatable). We can use collections of pointers to handle objects. But in this case note that we would have had to have had a very different design depending on whether we chose to have the collections own the object pointed at (i.e. responsible for deleting them) or not.
The choice made by the designers of the STL is the most versatile, but it also requires a greater degree of understanding. You have to be very careful of collections of pointers. You are probably better off with some form of smart-pointer with true copy semantics (auto_ptr will not do because of the ownership transfer semantics of its copy facilities) if you want to handle collections of objects.
It is probably hard to imagine how the STL could have been anything other than value based, but none-the-less it is important to explicitly understand this property if you are to use the STL correctly.
Now to that other question.
C++ provides two types of conversion, explicit ones via casts (either C-style, function style or new-style) or implicitly. For example:
int main (){ double d = 1.0; d = (double) 2; // C-style d = double(2); // function style d = static_cast<double> (3); // new-style d = 4; // implicit }
Note that the other new-style casts do something rather different and are not relevant here.
Implicit casts require the existence of either a standard conversion (such as those for the built-in types, derived to bass class etc.) or a sequence of such including not more than one user provided conversion. User provided conversions are of two kinds:
-
A conversion operator
-
A single argument constructor that has not been declared as explicit.
There are other possible conversions available for the three types of explicit cast but they are not relevant here, all you need to note is that explicit conversions are a strict superset of implicit ones. Also note that conversions are applied to values, not objects. Whenever the compiler is trying to resolve an expression it is free to try implicit conversions if no operators/functions have been supplied for the types of the constituent parts.
Let us now examine the assignment statement in question:
answer = Metre(len1-len2);
Consider the subexpression (len1-len2). The compiler checks to see if operator- has been provided for Metre types. It hasn't. So next it checks to see if there are any implicit conversions for Metre that convert values of Metre type to one for which an operator- has been defined. If it finds several possibilities, there are rules to try to resolve the choice. In this case it finds the conversion operator from Metre to double. It applies it to both len1 and len2 and completes the subtraction to get a double value.
Now if there were no cast it would look to see if there were an implicit conversion from double back to Metre. However the constructor that could do this conversion has been qualified as explicit.
The function style cast (constructor call) provides the explicit conversion from double to Metre so that the resulting value (temporary constructed by the explicit call) can be assigned to answer.
It is this liberty to search for conversion sequences (including at most one user defined conversion) that makes it so dangerous to provide conversion operators and single argument constructors that have not been qualified as explicit.
For a moment, consider the consequences of removing the explicit qualifier from the constructor for Metre:
int main(){ Metre length =5.0, breadth = 2.0; Metre perimeter; perimeter = length * breadth; cout << perimeter; }
I know the calculation is wrong but the compiler will not trap it because we will not have ensured that square metres cannot be implicitly converted to metres. Of course this is a pretty poor example but I hope it gives you an idea as to the dangers of providing implicit conversions for user defined types.
Notes:
More fields may be available via dynamicdata ..