Boost logo

Boost :

From: John Maddock (john_maddock_at_[hidden])
Date: 2003-01-28 07:03:54


Following the recent is_convertible discussion, I've put the following
together:

Rationale:
~~~~~~

There has been some fairly intense discussion on boost mailing list about
the is_convertible template, one suggestion was that since only expressions
(and not types) are convertible to a type or not, that is_convertible should
be an intrinsic binary operator whose first argument was an expression, and
whose second was a type. There are two key advantages to this approach: it
more closely mirrors the text of the standard, and therefore standard
wording is easier to formulate, and that the result can be context
sensitive:

class B;
class A
{
    A(const B&);
    /* details omitted */
};

Here class B is convertible to A only within the scope of A; if
is_convertible is a template then the one definition rule requires that
is_convertible<B,A>::value have one value only, and must not be sensitive to
context.

On the other hand, in spite of it's failings, the is_convertible template
has proven to be extremely useful in practice, and only very rarely does a
corner case arise where it fails to do the right thing. The template also
appears to be easily understood by end users, even if getting the
standardese correct is tricky. To put this in perspective there are about 50
uses of the is_convertible template within the boost library, none of which
would obviously benefit from an operator style of syntax, some examples
include:

lexical_cast: uses is_convertible to determine whether the source type can
be implicitly converted to the destination type, if it can then the usual
iostream based cast is optimised away.
Named template parameters: several libraries use is_convertible to determine
whether a template parameter is a particular named parameter, otherwise the
parameter is treated as a positional parameter.
Higher level traits: is_convertible is often used to help compose higher
level traits classes; for example the lambda library uses this to calculate
type promotions, this functionality could probably be more easily
implemented with a typeof operator however.
Static assertions: several libraries use is_convertible in conjunction with
static assertions to verify that template arguments have the required
properties, and to output an early, well commented error message if they do
not.

Likewise is_convertible has also been used (outside of boost) to ensure that
code "does the right thing", for example the constructor:

template <class T, class Allocator>
template <class I>
vector<T,Allocator>::vector<I>(I first, I last, const Allocator& a);

Needs to behave differently depending upon whether type I is an iterator or
a numeric type. One solution is to default initialise the object, and then
perform a compiler time dispatch to different a member function depending
upon whether the expression:

is_convertible<I,size_type>::value && is_convertible<I,value_type>::value

is true or not.

The author's belief is that as far as the DR is concerned, the
is_convertible template class is the right way to go; it is better
understood, and has shown itself to be useful as compared to an as yet
untried intrinsic operator. However implementers should be encouraged to
experiment with an intrinsic convertibility operator, and this subject may
need to be revisited prior to the next standard.

Text:
~~~

template <class From, class To> struct is_convertible {
   static const bool value = implementation_defined;
};

value: defined to be true only if an imaginary lvalue of type From is
implicitly-convertible to type To (4.0). Special conversions involving
string-literals and null-pointer constants are not considered (4.2, 4.10 and
4.11). No function-parameter adjustments (8.3.5) are made to type To when
determining whether From is convertible to To: this implies that a program
is ill formed if type To is a function type, and that if type To is an array
type, then value must necessarily evaluate to false.

The expression is_convertible<From,To>::value is ill-formed if:

Type From, is a void or incomplete type (3.9).

Type To, is not an object or reference type (3.9).

The conversion is ambiguous, for example if type From has multiple base
classes of type To (10.2).

Type To is of class type and the conversion would invoke a non-public
constructor of To (11.0 and 12.3.1).

Type From is of class type and the conversion would invoke a non-public
conversion operator of From (11.0 and 12.3.2).

~~~~

John Maddock
http://ourworld.compuserve.com/homepages/john_maddock/index.htm


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