isQualifierConvertible

Whether From is qualifier-convertible to To.

This is testing whether From and To are the same type - minus the qualifiers - and whether the qualifiers on From can be implicitly converted to the qualifiers on To. No other implicit conversions are taken into account.

For instance, const int* is not implicitly convertible to int*, because that would violate const. That means that const is not qualifier convertible to mutable. And as such, any const type is not qualifier convertible to a mutable type even if it's implicitly convertible. E.G. const int is implicitly convertible to int, because it can be copied to avoid violating const, but it's still not qualifier convertible, because const types in general cannot be implicitly converted to mutable.

The exact types being tested matter, because they need to be the same (minus the qualifiers) in order to be considered convertible, but beyond that, all that matters for the conversion is whether those qualifers would be convertible regardless of which types they were on. So, if you're having trouble picturing whether From would be qualifier convertible to To, then consider which conversions would be allowed from From[] to To[] (and remember that dynamic arrays are only implicitly convertible based on their qualifers).

The spec provides a table of which qualifiers can be implcitly converted to which other qualifers (and of course, there a bunch of examples below).

So, isQualifierConvertible can be used in a case like $(D isQualifierConvertible!(ReturnType!(typeof(foo(bar))), const char), which would be testing that the return type of foo(bar) was char, const char, or immutable char (since those are the only types which are qualifier convertible to const char).

This is in contrast to $(D isImplicitlyConvertible!(ReturnType!(typeof(foo(bar))), const char), which would be true for any type which was implicitly convertible to const char rather than just char, const char, and immutable char.

The single-argument overload makes it so that it can be partially instantiated with the first argument, which will often be necessary with template predicates.

  1. eponymoustemplate isQualifierConvertible(From, To)
    enum isQualifierConvertible (
    From
    To
    )
  2. template isQualifierConvertible(From)

Examples

1 // i.e. char* -> const char*
2 static assert( isQualifierConvertible!(char, const char));
3 
4 // i.e. const char* -> char*
5 static assert(!isQualifierConvertible!(const char, char));
6 
7 static assert( isQualifierConvertible!(int, int));
8 static assert( isQualifierConvertible!(int, const int));
9 static assert(!isQualifierConvertible!(int, immutable int));
10 
11 static assert(!isQualifierConvertible!(const int, int));
12 static assert( isQualifierConvertible!(const int, const int));
13 static assert(!isQualifierConvertible!(const int, immutable int));
14 
15 static assert(!isQualifierConvertible!(immutable int, int));
16 static assert( isQualifierConvertible!(immutable int, const int));
17 static assert( isQualifierConvertible!(immutable int, immutable int));
18 
19 // Note that inout gets a bit weird, since it's only used with function
20 // parameters, and it's a stand-in for whatever mutability qualifiers the
21 // type actually has. So, a function parameter that's inout accepts any
22 // mutability, but you can't actually implicitly convert to inout, because
23 // it's unknown within the function what the actual mutability of the type
24 // is. It will differ depending on the function arguments of a specific
25 // call to that function, so the same code has to work with all combinations
26 // of mutability qualifiers.
27 static assert(!isQualifierConvertible!(int, inout int));
28 static assert(!isQualifierConvertible!(const int, inout int));
29 static assert(!isQualifierConvertible!(immutable int, inout int));
30 static assert( isQualifierConvertible!(inout int, inout int));
31 
32 static assert(!isQualifierConvertible!(inout int, int));
33 static assert( isQualifierConvertible!(inout int, const int));
34 static assert(!isQualifierConvertible!(inout int, immutable int));
35 
36 // shared is of course also a qualifier.
37 static assert(!isQualifierConvertible!(int, shared int));
38 static assert(!isQualifierConvertible!(int, const shared int));
39 static assert(!isQualifierConvertible!(const int, shared int));
40 static assert(!isQualifierConvertible!(const int, const shared int));
41 static assert(!isQualifierConvertible!(immutable int, shared int));
42 static assert( isQualifierConvertible!(immutable int, const shared int));
43 
44 static assert(!isQualifierConvertible!(shared int, int));
45 static assert(!isQualifierConvertible!(shared int, const int));
46 static assert(!isQualifierConvertible!(shared int, immutable int));
47 static assert( isQualifierConvertible!(shared int, shared int));
48 static assert( isQualifierConvertible!(shared int, const shared int));
49 
50 static assert(!isQualifierConvertible!(const shared int, int));
51 static assert(!isQualifierConvertible!(const shared int, const int));
52 static assert(!isQualifierConvertible!(const shared int, immutable int));
53 static assert(!isQualifierConvertible!(const shared int, shared int));
54 static assert( isQualifierConvertible!(const shared int, const shared int));
55 
56 // Implicit conversions don't count unless they're based purely on
57 // qualifiers.
58 enum E : int
59 {
60     a = 1
61 }
62 
63 static assert(!isQualifierConvertible!(E, int));
64 static assert(!isQualifierConvertible!(E, const int));
65 static assert( isQualifierConvertible!(E, E));
66 static assert( isQualifierConvertible!(E, const E));
67 static assert(!isQualifierConvertible!(E, immutable E));
68 
69 static struct AliasThis
70 {
71     int i;
72     alias this = i;
73 }
74 
75 static assert(!isQualifierConvertible!(AliasThis, int));
76 static assert(!isQualifierConvertible!(AliasThis, const int));
77 static assert( isQualifierConvertible!(AliasThis, AliasThis));
78 static assert( isQualifierConvertible!(AliasThis, const AliasThis));
79 static assert(!isQualifierConvertible!(AliasThis, immutable AliasThis));
80 
81 // The qualifiers are irrelevant if the types aren't the same when
82 // stripped of all qualifers.
83 static assert(!isQualifierConvertible!(int, long));
84 static assert(!isQualifierConvertible!(int, const long));
85 static assert(!isQualifierConvertible!(string, const(ubyte)[]));

isQualifierConvertible can be used with partial instantiation so that it can be passed to a template which takes a unary predicate.

import phobos.sys.meta : AliasSeq, all, indexOf;

// byte is qualifier convertible to byte and const byte.
static assert(all!(isQualifierConvertible!byte, byte, const byte));

// const(char[]) at index 2 is the first type in the AliasSeq which string
// is qualifier convertible to.
alias Types = AliasSeq!(int, char[], const(char[]), string, int*);
static assert(indexOf!(isQualifierConvertible!string, Types) == 2);

See Also

Meta