-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathpromotions.yo
More file actions
118 lines (106 loc) · 4.98 KB
/
promotions.yo
File metadata and controls
118 lines (106 loc) · 4.98 KB
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
The function templates introduced in the previous section do not support
promotions. E.g., a statement like
verb(
result = rhs + 2;
)
generates a compilation error as promotions are not recognized by
the template argument deduction algorithm. Currently, the above statement needs
to be rewritten to have it accepted by the compiler:
verb(
result = rhs + Class{ 2 };
)
If promotions are welcome, how can we change the arithmetic operator
function templates so that promotions are performed? With promotions the
arguments of the operator functions may be of any type, at least one of them
must be of the class type offering the matching compound assignment
operator. But when designing the function template we can't say which of the
two operands has that class type. So we have to specify two template
types parameters for the two parameters of the operator functions. The
function template must therefore start with something like this:
verb(
template <typename LHS, typename RHS>
ReturnType operator+(LHS const &lhs, RHS const &rhs)
)
At this point we can't yet specify tt(ReturnType). It is tt(LHS) if
tt(RHS) can be promoted to or is equal to tt(LHS), it is be tt(RHS) if tt(LHS)
is promoted to tt(RHS).
To determine whether tt(RHS) can be promoted to tt(LHS) we now design a
simple template meta programming class tt(LpromotesR) using two template type
parameters, defining the value tt(true) (1) if the second (right hand) type
can be promoted to the first (left-hand) type, and defining the value
tt(false) (0) if not. Here we use the same principle seen before in section
ref(TYPECONV), link(type convertibility)(TYPECONV):
verb(
template <typename L, typename R>
class LpromotesR
{
struct Char2
{
char array[2];
};
static R const &makeR();
static char test(L const &);
static Char2 test(...);
public:
LpromotesR() = delete;
enum { yes = sizeof(test(makeR())) == sizeof(char) };
};
)
In tt(class LpromotesR) the function tt(test(L const &)) is selected if
tt(R) can be promoted to tt(L), and tt(test(...)) is selected if not. The
different sizes of the return types of these two tt(test) functions allows the
compiler to assign 1 or 0 to the class's tt(enum) value tt(yes). The value
tt(yes) (correctly) is 0 for tt(R) types mentioned in constructors declared
with the tt(explicit) keyword, and it (correctly) is 1 if tt(L) and tt(R)
happen to be the same types.
Now that we can determine whether a type can be promoted to another type,
it is possible to select either tt(LHS) or tt(RHS) as the function template's
return type. If tt(RHS) can be promoted to tt(LHS) use tt(LHS) as the
function template's return type, otherwise use RHS.
Of course there is a third possibility: the tt(LHS) and tt(RHS) types
cannot be used by each other's constructors. In that case, unless there is
another constructor lying around somewhere handling that situation, the
compiler generates an error like:
verb(
no match for 'operator+' in '...'
)
Back to promotable types. We are now able to determine which type can be
promoted, using tt(LpromotesR). This yields a value that can be used as
selector in the tt(IfElse) template meta programming class template,
introduced earlier (cf. section ref(IFELSE)).
Now that we can select either tt(LHS) or tt(RHS) as the operator template
function's return type, we're able to construct our arithmetic operator
function template supporting promotions:
verb(
template <typename LHS, typename RHS>
typename FBB::IfElse<FBB::LpromotesR<LHS, RHS>::yes, LHS, RHS>::type
operator<<(LHS const &lhs, RHS const &rhs)
{
typedef typename FBB::IfElse<
FBB::LpromotesR< LHS, RHS >::yes, LHS, RHS
>::type Type;
Type tmp(lhs);
return std::move(tmp) << type(rhs);
}
)
The function's return type is tt(IfElse)'s tt(type), selected as either
tt(LHS) (if tt(RHS) can be promoted to tt(LHS)) or tt(RHS). The same trick is
used in the function's body to determine tt(tmp)'s type.
Now promotions are possible. The function template defining an rvalue
reference parameter remains as-is. Together they allow the compiler to make
the following decisions (using tt(Class) as the intended class name, tt(Type)
as a type that is promotable to tt(Class), and tt(@) as the generic indication
of the used operator). Unless otherwise specified the function template
defining the parameter list tt((LHS const &lhs, RHS const &rhs)) is used:
verb(
Class obj;
Type value;
obj @ obj // no promotions
obj @ Class() // same
obj @ value; // value is promoted to Class
Class() @ value; // same
value @ obj; // same
value @ Class(); // same
Class() @ obj; // calls operator@(Class &&, Class const &)
Class() @ Class(); // same
)