mirror of
https://gitlab.com/fbb-git/cppannotations
synced 2024-11-16 07:48:44 +01:00
Added section about template aliases
git-svn-id: https://cppannotations.svn.sourceforge.net/svnroot/cppannotations/trunk@618 f6dd340e-d3f9-0310-b409-bdd246841980
This commit is contained in:
parent
8799ee27b7
commit
01e2ef8119
9 changed files with 142 additions and 165 deletions
|
@ -14,9 +14,6 @@ lsect(SUBTLE)(Subtleties)
|
|||
lsubsect(DOTTEMP)(::template, .template and ->template)
|
||||
includefile(advancedtemplates/dottemplate)
|
||||
|
||||
sect(Template aliases (C++11, 4.7)) TODO
|
||||
includefile(advancedtemplates/aliases)
|
||||
|
||||
sect(Template Meta Programming)
|
||||
|
||||
subsect(Values according to templates)
|
||||
|
@ -60,6 +57,9 @@ includefile(advancedtemplates/templateparam)
|
|||
subsect(Structure by Policy)
|
||||
includefile(advancedtemplates/structure)
|
||||
|
||||
lsect(ALIASES)(Template aliases (C++11, 4.7))
|
||||
includefile(advancedtemplates/aliases)
|
||||
|
||||
lsect(TRAIT)(Trait classes)
|
||||
includefile(advancedtemplates/trait)
|
||||
|
||||
|
|
|
@ -1,99 +1,64 @@
|
|||
Typedefs are commonly defined using the tt(typedef) keyword. In addition,
|
||||
C++11 allows the ti(using) keyword to associate a type and and identifier, but
|
||||
different from ti(typedef) it does em(not) define a type. In practice
|
||||
tt(typedef) and tt(using) can be used interchangeably.
|
||||
|
||||
In addition to function and class templates, C++11 also uses templates to
|
||||
define an alias for a set of types. This is called a emi(template
|
||||
alias). Template aliases can be specialized. The name of a template alias
|
||||
spacialization is a type name.
|
||||
define an alias for a set of types. This is called a
|
||||
emi(template alias). Template aliases can be specialized. The name of a
|
||||
template alias is a type name.
|
||||
|
||||
The name of a template alias is a template name.
|
||||
Template aliases can be used as arguments to template template
|
||||
parameters. This allows us to avoid the `unexpected default parameters' you
|
||||
may encounter when using template template parameters. E.g., defining a
|
||||
template specifying a tt(template <typename> class Container) is fine, but it
|
||||
is impossible to specify a container like tt(vector) or tt(set) as template
|
||||
template argument, as tt(vector) and tt(set) containers also define a second
|
||||
template parameter, specifying their allocation policy.
|
||||
|
||||
A template argument for a template template parameter is either a class
|
||||
template or it is a template alias (expressed as 'id expression'
|
||||
|
||||
Alias declarations cannot be (partially, explicitly) specialized. Template
|
||||
aliases can be specialized.
|
||||
|
||||
Examples:
|
||||
Template aliases are defined as tt(using) declarations, specifying an alias
|
||||
for an existing (maybe partially or fully specialized) template type. In the
|
||||
following example tt(Vector) is defined as an alias for tt(vector):
|
||||
verb(
|
||||
template <typename T> struct X {...};
|
||||
|
||||
template <typename T>
|
||||
using VecT = vector<T, Alloc<T>>;
|
||||
|
||||
Vec<int> vi; // same as vector<int, Alloc<int>> vi;
|
||||
|
||||
|
||||
--------------------
|
||||
template alias (formerly known as "template typedef")
|
||||
How can we make a template that's "just like another template" but possibly with a couple of template arguments specified (bound)? Consider:
|
||||
|
||||
|
||||
template<class T>
|
||||
using Vec = std::vector<T,My_alloc<T>>; // standard vector using my allocator
|
||||
|
||||
Vec<int> fib = { 1, 2, 3, 5, 8, 13 }; // allocates elements using My_alloc
|
||||
|
||||
vector<int,My_alloc<int>> verbose = fib; // verbose and fib are of the same type
|
||||
|
||||
The keyword using is used to get a linear notation "name followed by what it refers to." We tried with the conventional and convoluted typedef solution, but never managed to get a complete and coherent solution until we settled on a less obscure syntax.
|
||||
|
||||
Specialization works (you can alias a set of specializations but you cannot specialize an alias) For example:
|
||||
|
||||
template<int>
|
||||
struct int_exact_traits { // idea: int_exact_trait<N>::type is a type with exactly N bits
|
||||
typedef int type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct int_exact_traits<8> {
|
||||
typedef char type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct int_exact_traits<16> {
|
||||
typedef char[2] type;
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
template<int N>
|
||||
using int_exact = typename int_exact_traits<N>::type; // define alias for convenient notation
|
||||
|
||||
int_exact<8> a = 7; // int_exact<8> is an int with 8 bits
|
||||
|
||||
In addition to being important in connection with templates, type aliases can also be used as a different (and IMO better) syntax for ordinary type aliases:
|
||||
|
||||
|
||||
typedef void (*PFD)(double); // C style
|
||||
using PF = void (*)(double); // using plus C-style type
|
||||
using P = [](double)->void; // using plus suffix return type
|
||||
|
||||
-------------------------------------------------
|
||||
template <typename T>
|
||||
using Dictionary = std::map< std::string, T >;
|
||||
|
||||
Dictionary<int> ints;
|
||||
ints[ "one" ] = 1;
|
||||
ints[ "two" ] = 2;
|
||||
|
||||
---------------------- the really useful case:
|
||||
|
||||
#include <vector>
|
||||
|
||||
template <typename Type, template <typename> class Container>
|
||||
struct Wrapper: public Container<Type>
|
||||
{};
|
||||
|
||||
template <typename Type>
|
||||
using VT = std::vector<Type>;
|
||||
using Vector = std::vector<Type>;
|
||||
|
||||
int main()
|
||||
Vector<int> vi; // same as std::vector<int>
|
||||
std::vector<int> vi2(vi); // copy construction: OK
|
||||
)
|
||||
So, what's the point of doing this?
|
||||
Looking at the tt(vector) container, we see that it defines two, rather
|
||||
than one, template parameters, the second parameter being the allocation
|
||||
policy tt(_Alloc), by default set to tt(std::allocator<_Tp>):
|
||||
verb(
|
||||
template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
|
||||
class vector: ...
|
||||
)
|
||||
Now define a class template tt(Generic) defining a template template
|
||||
parameter:
|
||||
verb(
|
||||
template <typename Type, template <typename> class Container>
|
||||
class Generic: public Container<Type>
|
||||
{
|
||||
Wrapper<int, VT> wi;
|
||||
// Wrapper<int, std::vector> wi2; ERR!
|
||||
}
|
||||
...
|
||||
};
|
||||
)
|
||||
Most likely, tt(Generic) offers members made available by the container
|
||||
that is actually used to create the tt(Generic) object, and adds to those some
|
||||
members of it own. However, a simple container like tt(std::vector) cannot be
|
||||
used, as tt(std::vector) doesn't match a tt(template <typename> class
|
||||
Container>) parameter; it requires a tt(template <typename, typename> class
|
||||
Container>) template template parameter.
|
||||
|
||||
The tt(Vector) template alias, however, em(is) defined as a template
|
||||
having one template type parameter, and it uses the vector's default
|
||||
allocator. Consequently, passing a tt(Vector) to tt(Generic) works fine:
|
||||
verb(
|
||||
Generic<int, Vector> giv; // OK
|
||||
Generic<int, std::vector> err; // won't compile: 2nd argument mismatch
|
||||
)
|
||||
|
||||
With the aid of a small template alias it is also possible to use a
|
||||
completely different kind of container, like a tt(map), with tt(Generic):
|
||||
verb(
|
||||
template <typename Type>
|
||||
using MapString = std::map<Type, std::string>;
|
||||
|
||||
Generic<int, MapString> gim; // uses map<int, string>
|
||||
)
|
||||
|
||||
|
|
|
@ -29,7 +29,10 @@ parameter). It has the following elements:
|
|||
for containers). Programmers may not immediately realize that these
|
||||
defaults exist and be confused when the compiler rejects such
|
||||
templates when trying to pass them as template template parameters for
|
||||
which these additional (default) parameters weren't specified.
|
||||
which these additional (default) parameters weren't
|
||||
specified. However, the C++11 standard offers a solution for this
|
||||
problem in the form of em(template aliases), introduced in section
|
||||
ref(ALIASES).
|
||||
it() Following the bracketed list the keyword ti(class) em(must) be
|
||||
specified. In this case, tt(typename)
|
||||
hi(typename vs. class)
|
||||
|
@ -105,8 +108,8 @@ used), but here it is mentioned explicitly, allowing us to pass our own
|
|||
allocation policy to tt(Storage).
|
||||
|
||||
All required functionality is inherited from the tt(vector) base class, while
|
||||
the policy is `factored into the equation' via the template template
|
||||
parameter. Here's an example showing its use:
|
||||
the policy is `factored into the equation' using a template template
|
||||
parameter. Here's an example showing how this is done:
|
||||
verb(
|
||||
Storage<std::string, NewAlloc> storage;
|
||||
|
||||
|
|
|
@ -83,9 +83,6 @@ includefile(classtemplates/tuples)
|
|||
sect(Computing the return type of function objects (C++11))
|
||||
includefile(classtemplates/returntype)
|
||||
|
||||
sect(Template typedefs and `using' declarations (C++11, 4.7)) TODO
|
||||
includefile(classtemplates/templatetypedefs)
|
||||
|
||||
sect(Instantiating class templates)
|
||||
includefile(classtemplates/instantiations)
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ inline std::string &StringPtr::iterator::operator*() const
|
|||
//CMP
|
||||
inline bool StringPtr::iterator::operator<(iterator const &other) const
|
||||
{
|
||||
return **d_current < **other.d_current;
|
||||
return d_current < other.d_current;
|
||||
}
|
||||
//=
|
||||
//OPADD
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
The tt(typedef) keyword is frequently used to define `shorthand' notations
|
||||
for extensive type names. Consider the following example:
|
||||
verb(
|
||||
typedef
|
||||
std::map<std::shared_ptr<KeyType>, std::vector<std::set<std::string>>>
|
||||
MapKey_VectorStringSet;
|
||||
|
||||
MapKey_VectorStringSet mapObject;
|
||||
)
|
||||
In situations like this the actual type of the key or the data stored in
|
||||
the map's vector might vary. Some may store tt(string) objects, other may
|
||||
store tt(double) values.
|
||||
|
||||
A class may be defined based on the above tt(map) allowing the
|
||||
specification of the final key and value types. Example:
|
||||
verb(
|
||||
template <typename DeepKey, typename DeepValue>
|
||||
class DeepMap: public std::map<std::shared_ptr<DeepKey>,
|
||||
std::vector<std::set<DeepValue>>>
|
||||
{};
|
||||
)
|
||||
This increases the flexibility of the initially defined map as it is now
|
||||
possible to write
|
||||
verb(
|
||||
DeepMap<KeyType, std::string> mapObject;
|
||||
)
|
||||
The C++11 standard adds to this the possibility to pre-specify either of
|
||||
these template type parameters. To preset the key's type to tt(std::string)
|
||||
leaving the data type open for later specification the following
|
||||
emi(template using declaration) hi(using: template declaration) syntax is
|
||||
available:
|
||||
verb(
|
||||
template<typename DeepValue>
|
||||
using MapString_DeepValue = DeepMap<std::string, DeepValue>;
|
||||
|
||||
MapString_DeepValue<size_t> specialMap;
|
||||
)
|
||||
The tt(specialMap) now uses tt(shared_ptr<string>) for its key and
|
||||
tt(vector<set<size_t>>) for its data type. A similar shorthand notation can
|
||||
be used to pre-specify the value type leaving the key type open for later
|
||||
specification.
|
||||
|
||||
In addition to the above template syntax the ti(using) keyword
|
||||
can also be used to separate a typedef name from a complex type
|
||||
definition. Consider constructing a typedef defining a pointer to a function
|
||||
expecting and returning a tt(double). The traditional way to define such a
|
||||
type is as follows:
|
||||
verb(
|
||||
typedef double (*PFD)(double);
|
||||
)
|
||||
In addition the C++11 standard offers the following alternative syntax:
|
||||
verb(
|
||||
using PFD = double (*)(double);
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -43,6 +43,9 @@ sect(More extensions to C)
|
|||
lsubsect(AUTO)(Type inference using `auto' (C++11))
|
||||
includefile(first/typeinference)
|
||||
|
||||
subsect(Defining types and 'using' declarations (C++11, 4.7))
|
||||
includefile(first/using)
|
||||
|
||||
subsect(Range-based for-loops (C++11))
|
||||
includefile(first/rangebased)
|
||||
|
||||
|
|
65
yo/first/using.yo
Normal file
65
yo/first/using.yo
Normal file
|
@ -0,0 +1,65 @@
|
|||
In bf(C++) tt(typedef) is commonly used to define shorthand notations for
|
||||
complex types. Assume we want to define a shorthand for `a pointer to a
|
||||
function expecting a double and an int, and returning an unsigned long long
|
||||
int'. Such a function could be:
|
||||
verb(
|
||||
unsigned long long int compute(double, int);
|
||||
)
|
||||
A pointer to such a function has the following form:
|
||||
verb(
|
||||
unsigned long long int (*pf)(double, int);
|
||||
)
|
||||
If this kind of pointer is frequently used, consider defining it using
|
||||
tt(typedef): simply put tt(typedef) in front of it and the pointer's name is
|
||||
turned into the name of a type. It could be capitalized to let it stand out
|
||||
more clearly as the name of a type:
|
||||
verb(
|
||||
typedef unsigned long long int (*PF)(double, int);
|
||||
)
|
||||
After having defined this type, it can be used to declare or define such
|
||||
pointers:
|
||||
verb(
|
||||
PF pf = compute; // initialize the pointer to a function like
|
||||
// 'compute'
|
||||
void fun(PF pf); // fun expects a pointer to a function like
|
||||
// 'compute'
|
||||
)
|
||||
However, including the pointer in the typedef might not be a very good
|
||||
idea, as it masks the fact that tt(pf) is a pointer. After all, tt(PF pf)
|
||||
looks more like `tt(int x)' than `tt(int *x)'. To document that tt(pf) is
|
||||
in fact a pointer, slightly change the tt(typedef):
|
||||
verb(
|
||||
typedef unsigned long long int FUN(double, int);
|
||||
|
||||
FUN *pf = compute; // now pf clearly is a pointer.
|
||||
)
|
||||
The scope of typedefs is restricted to compilation units. Therefore,
|
||||
typedefs are usually embedded in header files which are then included by
|
||||
multiple source files in which the typedefs should be used.
|
||||
|
||||
In addition to tt(typedef) the C++11 standard offers the ti(using) keyword to
|
||||
associate a type and and identifier. In practice tt(typedef) and tt(using) can
|
||||
be used interchangeably. The tt(using) keyword arguably result in more
|
||||
readable type definitions. Consider the following three (equivalent)
|
||||
definitions:
|
||||
itemization(
|
||||
it() The traditional, bf(C) style definition of a type, embedding the type
|
||||
name in the definition (turning a variable name into a type name):
|
||||
verb(
|
||||
typedef typedef unsigned long long int FUN(double, int);
|
||||
)
|
||||
it() Apply tt(using) to improve the visibility (for humans) of the type
|
||||
name, by moving the type name to the front of the definition:
|
||||
verb(
|
||||
using FUN = unsigned long long int (double, int);
|
||||
)
|
||||
it() An alternative construction, using a late-specified return type
|
||||
(cf. section ref(AUTO)):
|
||||
verb(
|
||||
using FUN = auto (double, int) -> unsigned long long int;
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,19 +1,23 @@
|
|||
The keyword ti(typedef) is still allowed in bf(C++), but is not required
|
||||
The keyword ti(typedef) is still used in bf(C++), but is not required
|
||||
anymore when defining ti(union), ti(struct) or ti(enum) definitions.
|
||||
This is illustrated in the following example:
|
||||
verb(
|
||||
struct somestruct
|
||||
struct SomeStruct
|
||||
{
|
||||
int a;
|
||||
double d;
|
||||
char string[80];
|
||||
};
|
||||
)
|
||||
When a tt(struct), tt(union) or other compound type is defined, the tag of
|
||||
this type can be used as type name (this is tt(somestruct) in the above
|
||||
When a tt(struct, union) or other compound type is defined, the tag of
|
||||
this type can be used as type name (this is tt(SomeStruct) in the above
|
||||
example):
|
||||
verb(
|
||||
somestruct what;
|
||||
SomeStruct what;
|
||||
|
||||
what.d = 3.1415;
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue