Boost logo

Boost :

From: Aleksey Gurtovoy (alexy_at_[hidden])
Date: 2000-02-01 18:03:16


Dave Abrahams <abrahams_at_[hidden]> wrote:

> > | typedef boost::geometry2d::point<long> point;
> > | point p1;
> > | POINT p2( make_point<POINT>( p1 ) ); // as a conversion operator
> > | point p3( make_point<point>( p2 ) ); // as a constructor
> >
> > Does it make sense for you, Dave?
>
> I guess so, but the 2nd usage will need qualification. Koenig lookup can't
> help you there.
>

I was sure you will catch the error =). I wasn't very glad to realize that
we
need the qualification here, but after a few seconds I came to a some vague
idea how it probably can be done, so I left the error in code - to raise a
question...
The idea was to force a compiler to apply Koenig lookup by providing a
default argument, which would depends on 'make_point' return type -
something like

| namespace boost {
| namespace geometry2d {
| template<class T> T make_point( const POINT& p, T* = 0 ) {
| return T( p.x, p.y );
| }
| } // geometry2d
| } // boost
|
| void foo() {
| typedef boost::geometry2d::point<long> point;
| POINT p2;
| point p3( make_point<point>( p2 ) ); // probably illegal
| }

But now, after re-reading the standard, I think it will not work -
section 3.4.2 [basic.lookup.koenig], para 2, begins with 'For each
argument type T in the function call...' and the definition of 'argument'
(1.3.1 [defns.argument] ) declares that it is 'an expression in the
comma-separated list bounded by the parentheses in a function call
expression...' . Clearly enough, a default argument doesn't satisfy
this definition...

> I just had a kooky idea. We could get Koenig lookup to work even on
> broken-a**ed platforms like MSVC if we used operator overloading.
>
> what if you wrote:
>
> point p3 = point() << p2;
>
> This is a 'fill' operator. Is this usage just too darned idiomatic and
> strange?
>

Not for me ;). I think it has a quite natural (for C++) semantics. But
there are two issues with 'fill' operator:

1) its usage leads to a construction of one more temporary object -
comparing to 'make_point' function; but probably this is not a big
problem, as the temporary is not used at all, and compiler will
optimize it away (hopefully).

2) probably we need two versions of each 'fill' operator - one with a
'const' and another with a non-const reference to the first argument,
e.g.

| template<class T1, class T2>
| point<T1, T2>
| operator <<( const point<T1, T2>&, const POINT& from ) {
| return point<T1, T2>( from.x, from.y );
| }
|
| template<class T1, class T2>
| point<T1, T2>&
| operator <<( point<T1, T2>& to, const POINT& from ) {
| return to.x( from.x ).y( from.y );
| }

otherwise (with only 'const' reference version) the result of
following expression seems strange for me:

| point p1;
| POINT p2;
| //...
| p1 << p2; // doesn't change p1!!!

On the other hand, the usage of the second version of 'fill'
operator is more efficient than the usage of 'make_point'
function:

| p1 = make_point<POINT>( p2 );

So I think your idea is pretty nice :). I would like if we
decide to choose it as a part of working interface...

-Alexy


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk