Boost logo

Boost :

From: Paul Moore (gustav_at_[hidden])
Date: 2001-01-13 15:50:44


On 12 Jan 2001, at 20:15, Jens Maurer wrote:
> "Moore, Paul" wrote:
> > From: "Stephen Silver" <boost_at_[hidden]>
> > > template <class T> rational(T n) : num(), den(1) { num = n; }
> >
> > Good catch - I'll add this.
>
> Do make sure to document it right in the code. Any time I see such
> code as "default initialize + assign" in foreign code, I assume
> the code writer has not understood C++ at all.

Agreed. I looked at it a few times, and I'm still not 100% sure I
follow all of the implications. When I put it in, I got nasty ambiguity
errors, so I need to work on it a bit more.

There is a real issue here (the double implicit conversion), but it's
going to be important to avoid introducing more problems than I
solve...

> > Hmm. Overall, I agree with this, although I wonder whether there could be
> > cases where default-construct and assign is significantly slower than just
> > constructing. Seems far less likely than even the explicit constructor case,
> > though, and it's "only" performance, so I'd go with it.
>
> What about a fixed-size bigint based on a C array which needs to be
> default-initialized to 0 all over?

Yes. That's the example I couldn't quite think of...

> > Thinking some more, I suspect that we have to use this version as we must
> > avoid constructing a rational<T> from a type U when the only constructor
> > from U to T is explicit. So yes, I go with your code.
>
> It's ugly. It's a template implicit conversion. :-(

Agreed on both counts. If there's another solution, I'd prefer it. But I
do agree with the original issue - if I have a BigInt class which can
be used (almost) interchangably with int, it is extremely clumsy to
be unable to write

    rational<BigInt> r(1,2); // Works, only one UDC
    r *= 3; // Fails, double UDC

I feel that the second line should work. (Arguments as to why it
shouldn't, as opposed to why it doesn't, would be helpful).

> For the problem at hand, it seems sufficient to have a rational
> constructor which takes an "int" (or long) only. The template
> approach is only really needed if there is some non-builtin
> type U which can be converted to T, for example, another bigint
> class. And in this case, we arguably invoke two user-defined
> conversions (though technically speaking, we do it in two steps),
> i.e. from U to T and from T to rational<T>. Not my favourite
> perspective regarding complexity. Sorry, but listening to
> Steve Adamczyk at the Toronto ISO C++ meeting explaining auto_ptr
> implicit conversions built up a rather strong opinion against
> complex conversion chaining.

A constructor from long to rational<T> would be OK, but it would
cause an overload clash if T was long. So I don't think we can do
that.

If I understand your other point, you are saying that r *= 3 above
shouldn't compile because it involves a double UDC. Can you
please clarify, in terms of this example, what potential problems
there may be? Not problems with a double UDC (the language
won't allow taht, so if I want this code to compile, I need to avoid
the double UDC in any case), but rather what else r *= 3 could be
realistically expected to mean?

The reason I ask is that I am currently inclined to try to find a way
to make r *= 3 valid. If this is going to cause problems, I'd like to
know!

> I'd vote for the conservative "int only" approach.

I can accept that, if someone can offer a way round the issue for
rational<int>.

Paul.


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