2-0. | Write a unary metafunction add_const_ref<T> that returns T if it is a reference type, and otherwise returns T const&. Write a program to test your metafunction. Hint: you can use boost::is_same to test the results. |
2-1. | Write a ternary metafunction replace_type<c,x,y> that takes an arbitrary compound type c as its first parameter, and replaces all occurrences of a type x within c with y:
typedef replace_type< void*, void, int >::type t1; // int*
typedef replace_type<
int const*[10]
, int const
, long
>::type t2; // long* [10]
typedef replace_type<
char& (*)(char&)
, char&
, long&
>::type t3; // long& (*)(long&)
You can limit the function types you operate on to those with fewer than two arguments. |
2-2. | The boost::polymorphic_downcast function template implements a checked version of static_cast intended for downcasting pointers to polymorphic objects:
template <class Target, class Source>
inline Target polymorphic_downcast(Source* x)
{
assert( dynamic_cast<Target>(x) == x );
return static_cast<Target>(x);
}
In released software, the assertion disappears and polymorphic_downcast can be as efficient as a simple static_cast. Use the type traits facilities to write an implementation of the template that allows both pointer and reference arguments:
struct A {};
struct B : A {};
B b;
A* a_ptr = &b;
B* b_ptr = polymorphic_downcast<B*>(a_ptr);
A& a_ref = b;
B& b_ref = polymorphic_downcast<B&>(b_ref);
|
2-3. | Use the type traits facilities to implement a type_descriptor class template, whose instances, when streamed, print the type of their template parameters:
// prints "int"
std::cout << type_descriptor<int>();
// prints "char*"
std::cout << type_descriptor<char*>();
// prints "long const*& volatile"
std::cout << type_descriptor<long const*& volatile>();
You can assume that type_descriptor's template parameter is limited to compound types built from the following four integral types: char, short int, int, and long int. |
2-4. | Write an alternative solution for exercise 2-3 that does not use the Type Traits library. Compare the solutions. |
2-5. | Change the type_descriptor template from exercise 2-3 to output a pseudo-English description of the type, along the lines of the explain command of the cdecl program:
// prints "array of pointer to function returning pointer to "
// "char"
std::cout << type_descriptor< char *(*[])() >();
|
2-6*. | While at first sight the type algebra supplied by the Type Traits library might seem complete, it's not. There are at least a few type categories, relationships, and transformations that are not covered by the library's facilities. For example, they don't provide a way to get the corresponding unsigned counterpart of a signed integer type.
Try to identify as many of these missing parts as you canthere is at least one in each traits category, and we can think of at least 11 in all. Design an interface and come up with a motivating use case for each of the missing traits.
|
2-7*. | One of the nice things about touring the Type Traits library is that we also made a minitour of the C++ runtime type system. Each of the primary type categories, plus the const and volatile qualifiers, is a fundamental building block that can be used in constructing arbitrarily rich types.
All possible C++ types are possible "values" of type metadata, which leads to the question, "What does C++'s compile-time type system look like?" Write a short description of the static type system of compile time C++. Hint: a static type system restricts the values that can be passed to particular functions.
|
2-8*. | Describe the effect of making all metadata polymorphic in terms of static and dynamic type checking.
|