From: Öö Tiib on
On 23 apr, 02:18, David Barrett-Lennard <davi...(a)iinet.net.au> wrote:
> On Apr 22, 6:59 pm, �� Tiib <oot...(a)hot.ee> wrote:
> > I avoid it. In large project such implicit conversions cause sometimes
> > hard to discover bugs because they hide simple typos.
>
> Implicit conversions can be classified as safe or dangerous depending
> on whether there are surprises. I believe I gave a safe one that is
> very unlikely to be the source of bugs. Indeed all things being
> equal, more concise code tends to have fewer bugs. If you think
> otherwise I would appreciate a realistic example.

Ok. Imagine function parameter #3 of 6 is Rect, but you pass
innocently Squares to it (from some array) in cycle of 1000 that is in
cycle of 1000. Only profiler shows that there takes place million of
Rect constructions and simple optimization can get rid of it. Bare eye
does not see it in noteworthy project.

> > > 2) Is there a reason why C++ was designed so that const member
> > > functions defeat the implicit conversions on *this?
>
> > I don't think that they do. I think that your own const-correctness
> > and pass-by-value defects defeat it. Try if it works:
>
> Where was there a const-correctness defect?

There were no const correctness defects in posted code. There was no
code at all posted with const member functions. When *this does not
convert then const correctness bugs often cause it. So i said it about
imaginable code where you have that defeated conversion.

> Pass by value is hardly a "defect" (at least in the code I presented).

Depends. For me there are some things that i imagine as values and
other things that i imagine as objects. Objects i pass rarely by value
(and if i do then i comment there why). Values i pass rarely by
reference (and again if so then i comment there why). What is optimal
from performance standpoint may be different, but the idiom what i
described is optimal on majority of cases. "Rect" felt like example of
object class. Had it been "Duration" then it felt like example of
value class.

> Actually I meant that a const member function Rect::area() cannot
> allow implicit conversions when attempting to use it to find the area
> of a Square.

You want Square to behave like it had such member function that it
(and its base classes) actually lack? Then i misunderstood your
complaint. It is so in C++. Reason is again that if it did then it did
hide even more typos. C++ also does not let you to use chains of
implicit conversions. So if Square can be converted to Rect and Rect
to Polygon then that does not mean that you can use Square object as
parameter to functions that accept Polygon.

> The result is that in C++ free functions are much better
> behaved for value-types.

Free functions i use all the time. If something is possible to
implement as free, non-friend and non-member function effectively then
it is advisable to do so. It automatically lacks dependency on
implementation details and so it usually works after refactoring
without changes needed. Like you did show in your original post.

> Indeed this is a common reason for using
> friend functions.

Need for friend functions often indicates that you have some state of
class that should be externally visible but it is hidden. It may also
be that class has something that does not belong to it. Proper OO
design, encapsulation and hiding is tricky art to get right.


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

From: David Barrett-Lennard on
{ edits: linebreaks inserted in quotes. please keep readers in mind when
quoting. -mod }

On Apr 21, 2:46 pm, "Martin B." <0xCDCDC...(a)gmx.at> wrote:
> On 19.04.2010 14:04, David Barrett-Lennard wrote:
>
> > Consider that we implement simple datatypes (i.e. simple "value
> > types") without virtual functions and all read only functions are
> > global functions (instead of class member functions). Let single
> > argument constructors be used to support implicit type conversions.
> > E.g. to emulate Square value is-a Rect value.
>
> Well. Except that it "isn't" - not in the sense that we are supposed
> to(?) understand is-a, namely that everything (interface-wise) you can
> *do* with one thing has to be doable with the other thing. (in your
> example the interface of Rect implicitly contains the possibility to
> change w and h, which Square doesn't.)

I agree that C++ programmers tend to interpret is-a (and sub-typing)
in terms of state machine substitutability (see my second post).
However I think most C++ programmers would agree that every square
*value* is a rectangle *value* (which is what I said).


> However, I think your example cleverly captures is-representable-by -
> or rather you capture, not that a Square "is-a" Rect, but that a
> Square can be (losslessly) converted to a Rect.

It would be a lot simpler if you distinguish between variables and
values. Then just say Square value is a Rectangle value. It's also
clear that a Square variable isn't a Rectangle variable (and vice
versa).

I don't think C++ programmers should assume that any sentence
containing the word "square" in it denotes a variable. E.g. when I
say "a square of area 4" I'm not referring to a variable! I say that
because I think it is a little mischievous for a C++ programmer to
claim it's incorrect to say a square is a rectangle (as if we're only
allowed to talk about variables).

I suggest as a matter of principle that implicit conversions are only
sensible between value types, and they should only be permitted when
they represent alternative encodings of the identical abstract value.
This implies implicit conversions should neither add nor lose
information.


> > The following uses structs (public member variables) without
> > suggesting that is necessarily appropriate.
>
> > struct Square { int s; };
> > int side(Square sq) { return sq.s; }
>
> > struct Rect
> > {
> > Rect(Square s) : w(side(s)), h(side(s)) {}
> > int w, h;
> > };
> > int width(Rect r) { return r.w; }
> > int height(Rect r) { return r.h; }
> > int area(Rect r) { return width(r) * height(r); }
>
> > void Test()
> > {
> > Square s = {10};
> > int A = area(s);
> > }
>
> > The width, height and area functions defined on rectangle values are
> > also available for square values, which is just what we need, and
> > could be viewed as a form of inheritance. (...)
>
> It could. With the right audience. I fear the average listener might
> be slightly confused though.

Yes, it seems that C++ programmers reserve the term "inheritance" for
when subclassing is involved.


> I think the example shows that there can be value in defining "interface"
> functions such that if they only operate on public data there may be
> arguments for making them free functions rather than member functions.

That's not the reason for using free functions. See the example
below.


> > Square inherits functions defined on rectangles but not the
> > implementation of rectangle (i.e. the member variables). (...)
>
> While I would agree that on a certain conceptual level "inheritance"
> seems an appropriate term, I also think that it already has a predefined
> meaning that doesn't quite fit your example.

What exactly is the predefined meaning? E.g. is it inheritance of
interface, inheritance of member variables, inheritance of member
functions, or all of these?


> > Questions:
>
> > 1) Is this a reasonable technique?
>
> It looks neat and I'm sure there are a few cases where it is very
> reasonable. I just wouldn't call it inheritance.
>
> > 2) Is there a reason why C++ was designed so that const member
> > functions defeat the implicit conversions on *this?
>
> I'm sure I don't know what you mean by that??

Slightly modified example:

struct Rect
{
Rect(Square s) : w(side(s)), h(side(s)) {}
int area() const { return w*h; }
int w, h;
};

void Test()
{
Square s = {10};
int A = s.area(); // error
}

The compiler won't perform implicit conversions in this case.

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

From: Keith H Duggar on
On Apr 19, 8:04 am, David Barrett-Lennard <davi...(a)iinet.net.au>
wrote:
> Consider that we implement simple datatypes (i.e. simple "value
> types") without virtual functions and all read only functions are
> global functions (instead of class member functions). Let single
> argument constructors be used to support implicit type conversions.
> E.g. to emulate Square value is-a Rect value.
>
> The following uses structs (public member variables) without
> suggesting that is necessarily appropriate.
>
> struct Square { int s; };
> int side(Square sq) { return sq.s; }
>
> struct Rect
> {
> Rect(Square s) : w(side(s)), h(side(s)) {}
> int w, h;
> };
> int width(Rect r) { return r.w; }
> int height(Rect r) { return r.h; }
> int area(Rect r) { return width(r) * height(r); }
>
> void Test()
> {
> Square s = {10};
> int A = area(s);
> }
>
> The width, height and area functions defined on rectangle values are
> also available for square values, which is just what we need, and
> could be viewed as a form of inheritance. By declaring these
> functions inline a modern compiler will typically eliminate
> temporaries and be as efficient as a specialisation for Square.

It's possible is /could/ be viewed as a "form" of inheritance,
but that would of course depend on how you define inheritance.
It certainly is not inheritance in the C++ definition. And no
matter what definition you choose, if it allowed this to be a
"form of inheritance" I would argue that said definition is at
total odds with any common sense meaning of "inheritance".

It is simply entirely unnecessary to introduce such notions of
"inheritance" /at all/ in this case. Instead let's view Square
and Rect as distinct types in a multi-sorted algebra that just
happen to have coercion relations.

Suppose I create a coercion from complex<int,int> to rectangle,
does complex<int,int> now "inherit" from rectangle? What if we
add a coercion from rectangle to complex<int,int>, does rectangle
no circularly inherit from complex? What about all the numerous
other types we made add in future, are we now talking about
multiple inheritance? Not anywhere close in my mind. How would
such "inheritance" thinking or terminology even be useful?

> Square inherits functions defined on rectangles but not

No it doesn't "inherit" the functions. Instead you have created
an algebra in which an implicit coercion makes syntax such as

width(square)

valid. Suppose I now declare and explicit function for square

int area ( Square s ) { return side(s) * side(s) ; }

would you now propose to say that I have "overridden the
inherited (from rectangle) area function"? That doesn't make
any bit of sense to me. "inheritance" has no place here.

> the implementation of rectangle (i.e. the member variables).
> In fact Square can be implemented in any way we like. E.g. by
> recording the perimeter.
>
> struct Square { int perim; };
> int side(Square sq) { return sq.perim/4; }

Indeed that is one very nice result of increasing modularity
like this by using free functions heavily and /not/ trying to
force hierarchical notions such as "inheritance" onto the code.

> Note that the implicit conversions are still available if the read
> only functions use const references. E.g. area could be declared like
> this
>
> int area(const Rect& r)
> {
> return width(r) * height(r);
> }
>
> Questions:
>
> 1) Is this a reasonable technique?

Yes I think so. In my opinion this is /very/ reasonable and
"good". Unfortunately we are forced to make certain functions
(constructors, destructors, conversion operators, etc) member
functions. So there are inherent asymmetries in C++ that cannot
be avoided. In response we see idioms such as 'as<...>()',
'make<...>()', 'cast<...>()', etc have sprung to life (often
to support generic programming).

KHD

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

From: Keith H Duggar on
On Apr 22, 9:09 am, David Barrett-Lennard <davi...(a)iinet.net.au>
wrote:
> On Apr 22, 3:35 am, Keith H Duggar <dug...(a)alum.mit.edu> wrote:
> > And no
> > matter what definition you choose, if it allowed this to be a
> > "form of inheritance" I would argue that said definition is at
> > total odds with any common sense meaning of "inheritance".
>
> I was thinking of it as "inheritance" in the following sense: Let a
> data type mean a set of abstract values plus operators on those values
> (using operator in an algebraic sense). The "behaviour" of a datatype
> is only externally visible through those operators. Relating this
> back to C++ code, assuming all operators are expressed as free
> functions without in-out parameters, an implicit conversion between
> two datatypes means that all the operators of one are available to the
> other - because of value substitutability. I think it is reasonable
> to call that "inheritance" treating the word as simply meaning that
> "stuff" associated with one "thing" is automatically available to
> another "thing". Anyway that was the only point I wanted to make! I
> just looked for synonyms of "inherit" at synonym.com and the only
> alternatives it gave me are "get" and "acquire".
>
> > What about all the numerous
> > other types we made add in future, are we now talking about
> > multiple inheritance?
>
> In the sense of inheritance I described above, definitely!

Then as they say "Houston we have a problem" ;-) That is because
traditionally (and I personally haven't seen any exceptions to
this so I would tentatively say "universally") the "inheritance"
concept in both type theory and practical programming, is thought
of if not /defined/ as a partial ordering. And in that case we
have the antisymmetry law of partial order logic ie

x <: y AND y <: x IMPLIES x = y

where "<:" is "inherits from" So, if we take you view that it's
reasonable for (since you allow coercions define "inheritance")

Rect <: Square
Square <: Rect

then

Square = Rect

which is obviously false. So I just think it's very unwise from
a communication perspective to call '"stuff" associated with one
"thing" is automatically available to another "thing"' inheritance.
Other phrases such as convertible, compatible, associated, etc come
to mind. Perhaps there is a traditional word for it in algebraic
type theory but I can't recall.

> > Suppose I create a coercion from complex<int,int> to rectangle,
> > does complex<int,int> now "inherit" from rectangle? What if we
> > add a coercion from rectangle to complex<int,int>, does rectangle
> > no circularly inherit from complex?
>
> That's just weird so I'm not sure what your point is here.

Geometric interpretation of complex is VERY common. And the
interpretation of a vector defining a corner of a rectangle
rooted at the origin is also VERY common. So I don't see why
you call this "weird". But this is a nit anyhow.

> A better
> example for me would be classes named Polar and Cartesian and we would
> like implicit conversions in both directions. I haven't studied the
> rules of implicit conversions for C++ to know whether such a thing
> works in practise, but in principle I can't see any problems with
> alternative representations of abstract values.

Sure you can define implicit conversion in both directions
(for user defined types anyhow).

> BTW the conversions between polar and cartesian representations
> involve cos, sin, sqrt, atan2 which are inexact for rational numbers
> (including floats). That could mean we have an example of the
> following assertion breaking which I find extremely unappealing:
>
> T1 x;
> T2 y = x;
> assert(x == y);
>
> This can fail if coercions aren't invertible and y is coerced in order
> to perform comparison using T1::operator==. On that basis I would
> reject implicit conversions between polar and cartesian
> representations.

Meh, that's an implementation issue that can be solved in a
variety of ways.

> I would hope that implicit conversions between datatypes satisfy
> reasonable "laws" to make program correctness easy to reason about.
>
> Unfortunately C++ doesn't give you a transitive closure of the

Well, unfortunately or fortunately depending on your viewpoint,
the standards committee decided that disallowing such transitive
closure was a "reasonable law" to help ensure correctness. Maybe
one of the members will comment. I vaguely recall this briefly
discussed in either D&E or ARM but I can't be asked to track that
reference down right now. (I need grep'able copies of my books LOL).

> defined implicit conversions. Consider that we extend my previous
> example with an additional type to represent an arbitrary
> quadrilateral and we recognise that square value is-a rectangle value
> is-a quadrilateral value.

*sigh* you keep forcing the "is-a" thinking in. Do you really see
is-a as a necessary concept? Even a particularly helpful one? Do
you not see any value in my suggestion to drop that hierarchical
thinking in favor of "flatter" relational thinking ie algebraic
type theory?

> > Not anywhere close in my mind. How would
> > such "inheritance" thinking or terminology even be useful?
>
> In the case of square value isa rectangle value, it is useful in the
> sense that it is a reminder that one doesn't necessarily need to
> implement operators on square that have already been written for
> rectangles. If one forgets that then one may end up writing more code
> than needed (and the C++ compiler won't complain of course).

Is that it?? A useful reminder? Sorry, that seems quite a tiny
(if any) benefit to force "is-a" on to every (perhaps any)
analysis of syntax or concepts.

For example, it is far more useful to keep in mind that division
is not closed over integers than it is to sing "an integer is-a
rational" all day long. The first viewpoint emphasizes knowing
the algebra, the second knowing some shoe-horned "hierarchy".

> > > Square inherits functions defined on rectangles but not
>
> > No it doesn't "inherit" the functions. Instead you have created
> > an algebra in which an implicit coercion makes syntax such as
>
> > width(square)
>
> > valid. Suppose I now declare and explicit function for square
>
> > int area ( Square s ) { return side(s) * side(s) ; }
>
> > would you now propose to say that I have "overridden the
> > inherited (from rectangle) area function"? That doesn't make
> > any bit of sense to me. "inheritance" has no place here.
>
> I'm not sure why. I thought the "override" metaphor was quite a good
> one. I would however say it's a very dangerous analogy because it
> means something quite different to overrides of virtual methods in the
> context of subclassing. However, on the other hand we are talking

Yes, that was careless. I was using "override" in the type theory
usage forgetting that it has a restricted meaning in C++.

> about data types here and we know that subclassing is completely and
> universally useless for data types so I would suggest that confusion
> cannot occur for C++ programmers that properly understand datatypes.

Eh, I would rather just avoid words like "inheritance" for this
mechanism since it already has such a strong nearly universal
connotation of ordering that does not apply here.

> I would say that such an "override" must not be allowed to change the
> externally visible behaviour, but it can be useful to optimise
> performance when the compiler is inadequate.
>
> For any data types T1,T2 and unary operator foo I don't believe the
> assertion below should be allowed to fail:
>
> T1 x;
> T2 y = x;
> assert(foo(x) == foo(y));
>
> "overrides" would need to respect that (for example).

Sure I agree there are common sense semantic axioms one should
respect which apply to types in general.

KHD

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

From: Öö Tiib on
On Apr 23, 4:29 pm, David Barrett-Lennard <davi...(a)iinet.net.au>
wrote:
> On Apr 23, 12:55 pm, �� Tiib <oot...(a)hot.ee> wrote:
>
> > On 23 apr, 02:18, David Barrett-Lennard <davi...(a)iinet.net.au> wrote:
>
> > > On Apr 22, 6:59 pm, �� Tiib <oot...(a)hot.ee> wrote:
> > > > I avoid it. In large project such implicit conversions cause sometimes
> > > > hard to discover bugs because they hide simple typos.
>
> > > Implicit conversions can be classified as safe or dangerous depending
> > > on whether there are surprises. I believe I gave a safe one that is
> > > very unlikely to be the source of bugs. Indeed all things being
> > > equal, more concise code tends to have fewer bugs. If you think
> > > otherwise I would appreciate a realistic example.
>
> > Ok. Imagine function parameter #3 of 6 is Rect, but you pass
> > innocently Squares to it (from some array) in cycle of 1000 that is in
> > cycle of 1000. Only profiler shows that there takes place million of
> > Rect constructions and simple optimization can get rid of it. Bare eye
> > does not see it in noteworthy project.
>
> That's not a bug though.

No. I did not say it is a bug. I said that i usually avoid it
(implicit conversions). With modern editors helping to type and with
1920 pixels of vertical space it is not so wasteful to indicate every
conversion explicitily. It comments very well what is going on.

>
> You seem to be saying that a general purpose algorithm shouldn't be
> implicitly available for special cases because it may be suboptimal.
> I find that idea highly questionable, particularly when it is easy to
> specialise the algorithm as and when profiling indicates that it is
> appropriate to do so.

I did mean that implicit conversion hides that conversion takes place.
Conversion may cause bugs or suboptimal code, but avoiding it because
of that is premature optimization. I simply indicate it explicitly and
all. That saves time of possible reader who is likely looking for
cause of bug or possibility of optimization one day (often myself).

> > > Pass by value is hardly a "defect" (at least in the code I presented).
>
> > Depends. For me there are some things that i imagine as values and
> > other things that i imagine as objects. Objects i pass rarely by value
> > (and if i do then i comment there why). Values i pass rarely by
> > reference (and again if so then i comment there why). What is optimal
> > from performance standpoint may be different, but the idiom what i
> > described is optimal on majority of cases. "Rect" felt like example of
> > object class. Had it been "Duration" then it felt like example of
> > value class.
>
> I'm unsure what you mean by "object". You seem to imply that objects
> are not values and yet it could be possible (albeit "rarely") to pass
> an object by value. I don't know what that means.

"object" as abstraction of discrete item. Noun.
Object has interface to observe and to manipulate it. Verb. Command.
Query.
"value" as property or relation of object or helper in interface.
Adjective. Adverb. Preposition. Quantity. Property.
That is my silly view on imperative object oriented programming. I
create army of objects, observe and command them. Let them to observe
and command each other for me. Silly, because in real languages same
words take different parts of speech depending on context, but i
ignore it.

>
> I tend to avoid the word "object" because it is a source of
> confusion. I consider functions to support pass by value, or else
> pass by reference to a variable or state machine. I consider pass by
> const reference to mean passing a reference to a variable or state-
> machine that must be considered immutable.

I imagine it so that if i pass some discrete item ("object") by value
then i clone that item behind scenes by language rules. So i feel need
to indicate it that i did it. However when it is support information
("value") then it is better to copy it. Giving reference to original
property attached to some discrete object feels wrong (so again i feel
need to indicate it).

You seems to look at things differently. I do not say that it is
wrong. Only that it is weakly supported by language mechanics, that
does not let you do some things that you want and might do other
things that spoil the effect.


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