1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
|
Alternatively the following syntax can be used:
verb(
template< typename Type> requires ComparableBySmaller<Type>
Type const &min(Type const &x, Type const &y)
{
return y < x ? y : x;
}
)
The advantage of the former syntax is that it allows a list of
requirements, e.g., linebreak() tt(ComparableBySmaller<Type>,
Ordered<Type>). Moreover, these requirements may be combined using the
logical negation (! (tt(not))) and conjunction (&& (tt(and))) operators.
To define a concept hi(concept: definition) the following syntax will be
provided:
verb(
auto concept ComparableBySmaller <typename Type>
{
bool operator<(Type, Type);
}
)
This concept specifies that a type supporting an tt(operator<) accepting
two tt(Type) objects and returning a tt(bool) is a tt(ComparableBySmaller)
type. Note that this therefore becomes an implied characteristic of an actual
type. Any type defining this operator by implication also becomes a
tt(ComparableBySmaller) type. Any such type may therefore be used to
instantiate a template with that requires the tt(ComparableBySmaller) concept.
The C++ standard introduces emi(axiom)em(s) as a feature of concepts. Axioms
can be used to define properties of concepts, such as their
associativity. Here is a preliminary example of how an axiom could be used in
the definition of a concept:
verb(
concept Semigroup< typename Op, typename Type> : CopyConstructible<Type>
{
Type operator()(Op, Type, Type);
axiom Associativity(Op op, Type x, Type y, Type z)
{
op(x, op(y, z)) == op(op(x, y), z);
}
};
)
As remarked, concepts and axioms are currently still unavailable in the
tt(g++) compiler. It is likely that this section will be vastly expanded by
the time the compiler has fully implemented the C2a standard. Concepts, axioms
and related constructs may eventually even be covered in a separate chapter.
COMMENT(
=================================================================
The
operator need not be a free-function; it could be a member function of the
type T.
Concepts can involve multiple objects as well. For example, concepts can
express that a type is convertible from one type to another:
auto concept Convertible< typename T, typename U>
{
operator U(const T&);
}
In order to use this in a template, it must use a generalized form of concept
usage:
template< typename U, typename T> requires Convertible<T, U>
U convert(const T& t)
{
return t;
}
Concepts may be composed. For example, given a concept named Regular:
concept InputIterator< typename Iter, typename Value>
{
requires Regular<Iter>;
Value operator*(const Iter&);
Iter& operator++(Iter&);
Iter operator++(Iter&, int);
}
The first template parameter to the InputIterator concept must conform to the
Regular concept.
Concepts can also be derived from one another, like inheritance. Like in class
inheritance, types that meet the requirements of the derived concept also meet
the requirements of the base concept. It is defined as per class derivation:
concept ForwardIterator< typename Iter, typename Value> : InputIterator<Iter,
Value> {
//Add other requirements here.
}
Typenames can also be associated with a concept. These impose the requirement
that, in templates that use those concepts, these typenames are available:
concept InputIterator< typename Iter>
{
typename value_type;
typename reference;
typename pointer;
typename difference_type;
requires Regular<Iter>;
requires Convertible<reference, value_type>;
reference operator*(const Iter&); // dereference
Iter& operator++(Iter&); // pre-increment
Iter operator++(Iter&, int); // post-increment
//...
}
Concept maps allow types to be explicitly bound to a concept. They also allow
types to, where possible, adopt the syntax of a concept without changing the
definition of the type. As an example:
concept_map InputIterator<char*>
{
using value_type = char;
using reference = char &;
using pointer = char *;
using difference_type = std::ptrdiff_t;
};
This map fills in the required typenames for the InputIterator concept when
applied to char* types.
As an added degree of flexibility, concept maps themselves can be
templated. The above example can be extended to all pointer types:
template< typename T> concept_map InputIterator<T*>
{
using value_type = T;
using reference = T &;
using pointer = T *;
using difference_type = std::ptrdiff_t;
};
Further, concept maps can act as mini-types, with function definitions and
other constructs commonly associated with classes:
concept Stack< typename X>
{
typename value_type;
void push(X&, const value_type&);
void pop(X&);
value_type top(const X&);
bool empty(const X&);
};
template< typename T> concept_map Stack<std::vector<T> >
{
using value_type = T;
void push(std::vector<T>& v, const T& x) { v.push_back(x); }
void pop(std::vector<T>& v) { v.pop_back(); }
T top(const std::vector<T>& v) { return v.back(); }
bool empty(const std::vector<T>& v) { return v.empty(); }
};
This concept map allows templates that take types that implement the concept
Stack to take a std::vector, remapping the function calls directly to the
std::vector calls. Ultimately, this allows a pre-existing object to be
converted, without touching the definition of the object, into an interface
that a template function can utilize.
Finally, it should be noted that some requirements can be checked using static
assertions. These can verify some requirements that templates need, but are
really aimed at a different problem.
================================================================
Compilers are allowed, but not required, to take advantage of the semantics
specified by axioms to perform optimizations that possibly have side-effects
on the observable behavior of the program, which are typically prohibited
(with few exceptions such as copy constructor elision). In the above example,
compilers may reassociate nested calls to operator() of type Op on several
values of type Type provided that there is a concept map for types Op and Type
to the concept Semigroup.
Axioms can also assist in software verification, software testing, and other
program analyses and transformations.
===========================================================
END)
|