1 static assert(is(Unqualified!( int) == int)); 2 static assert(is(Unqualified!( const int) == int)); 3 static assert(is(Unqualified!( inout int) == int)); 4 static assert(is(Unqualified!( inout const int) == int)); 5 static assert(is(Unqualified!(shared int) == int)); 6 static assert(is(Unqualified!(shared const int) == int)); 7 static assert(is(Unqualified!(shared inout int) == int)); 8 static assert(is(Unqualified!(shared inout const int) == int)); 9 static assert(is(Unqualified!( immutable int) == int)); 10 11 // Only the outer layer of immutable is removed. 12 // immutable(int[]) -> immutable(int)[] 13 alias ImmIntArr = immutable(int[]); 14 static assert(is(Unqualified!ImmIntArr == immutable(int)[])); 15 16 // Only the outer layer of const is removed. 17 // const(int*) -> const(int)* 18 alias ConstIntPtr = const(int*); 19 static assert(is(Unqualified!ConstIntPtr == const(int)*)); 20 21 // const(int)* -> const(int)* 22 alias PtrToConstInt = const(int)*; 23 static assert(is(Unqualified!PtrToConstInt == const(int)*)); 24 25 // Only the outer layer of shared is removed. 26 // shared(int*) -> shared(int)* 27 alias SharedIntPtr = shared(int*); 28 static assert(is(Unqualified!SharedIntPtr == shared(int)*)); 29 30 // shared(int)* -> shared(int)* 31 alias PtrToSharedInt = shared(int)*; 32 static assert(is(Unqualified!PtrToSharedInt == shared(int)*)); 33 34 // Both const and shared are removed from the outer layer. 35 // shared const int[] -> shared(const(int))[] 36 alias SharedConstIntArr = shared const(int[]); 37 static assert(is(Unqualified!SharedConstIntArr == shared(const(int))[])); 38 39 static struct S 40 { 41 int* ptr; 42 const int* cPtr; 43 shared int* sPtr; 44 } 45 46 shared const S s; 47 static assert(is(typeof(s) == shared const S)); 48 static assert(is(typeof(typeof(s).ptr) == shared const int*)); 49 static assert(is(typeof(typeof(s).cPtr) == shared const int*)); 50 static assert(is(typeof(typeof(s).sPtr) == shared const int*)); 51 52 // For user-defined types, all qualifiers that are applied to member 53 // variables only because the containing type has them are removed, but the 54 // ones that are directly on those member variables remain. 55 56 // shared const S -> S 57 static assert(is(Unqualified!(typeof(s)) == S)); 58 static assert(is(typeof(Unqualified!(typeof(s)).ptr) == int*)); 59 static assert(is(typeof(Unqualified!(typeof(s)).cPtr) == const int*)); 60 static assert(is(typeof(Unqualified!(typeof(s)).sPtr) == shared int*)); 61 62 static struct Foo(T) 63 { 64 T* ptr; 65 } 66 67 // The qualifiers on the type are removed, but the qualifiers on the 68 // template argument are not. 69 static assert(is(Unqualified!(const(Foo!(const int))) == Foo!(const int))); 70 static assert(is(Unqualified!(Foo!(const int)) == Foo!(const int))); 71 static assert(is(Unqualified!(const(Foo!int)) == Foo!int));
Removes the outer layer of all type qualifiers from type T - this includes shared.
If no type qualifiers have been applied to the outer layer of type T, then the result is T.
For the built-in scalar types (that is bool, the character types, and the numeric types), they only have one layer, so const U simply becomes U.
Where the layers come in is pointers and arrays. const(U*) becomes const(U)*, and const(U[]), becomes const(U)[]. So, a pointer goes from being fully const to being a mutable pointer to const, and a dynamic array goes from being fully const to being a mutable dynamic array of const elements. And if there are multiple layers of pointers or arrays, it's just that outer layer which is affected - e.g. shared(U**) would become shared(U*)*.
For user-defined types, the effect is that const U becomes U, and how that affects member variables depends on the type of the member variable. If a member variable is explicitly marked with any qualifiers, then it will continue to have those qualifiers even after Unqualified has stripped all qualifiers from the containing type. However, if a qualifier was on the member variable only because the containing type had that qualifier, then when Unqualified removes the qualifier from the containing type, it is removed from the member variable as well.
Also, Unqualified has no effect on what a templated type is instantiated with, so if a templated type is instantiated with a template argument which has a type qualifier, the template instantiation will not change.
Note that in most cases, Unconst or Unshared should be used rather than Unqualified, because in most cases, code is not designed to work with shared and thus doing type checks which remove shared will allow shared types to pass template constraints when they won't actually work with the code. And when code is designed to work with shared, it's often the case that the type checks need to take const into account in order to avoid accidentally mutating const data and violating the type system.
In particular, historically, a lot of D code has used std.traits.Unqual (which is equivalent to phobos.sys.traits' Unqualified) when the programmer's intent was to remove const, and shared wasn't actually considered at all. And in such cases, the code really should use Unconst instead.
But of course, if a template constraint or static if really needs to strip off both the mutability qualifiers and shared for what it's testing for, then that's what Unqualified is for. It's just that it's best practice to use Unconst when it's not clear that shared should be removed as well.
Also, note that is(immutable T == immutable U)) is equivalent to is(Unqualified!T == Unqualified!U) (using immutable converts const, inout, and shared to immutable, whereas using Unqualified strips off all type qualifiers, but the resulting comparison is the same as long as immutable is used on both sides or Unqualified is used on both sides)). So, in cases where code needs to compare two types to see whether they're the same while ignoring all qualifiers, it's generally better to use immutable on both types rather than using Unqualfied on both types, since that avoids needing to instantiate a template, and those instantiations can really add up when a project has a lot of templates with template constraints, static ifs, and other forms of conditional compilation that need to compare types.