27. Exceptions
28.5 Attributes for interoperation
30.3.2 Type deduction
This subclause uses both the terms "type deduction" and "type inference". "Type deduction" should be used uniformly.[[#Ed.]]
A call to a generic function can explicitly specify a type argument list via a generic-id, or it can omit that 25
type argument list using a generic-name only and rely on type deduction to determine the type arguments. [Example: ref struct X { generic<typename T> static void F(T t) { 30 Console::WriteLine("one"); } generic<typename T> static void F(T t1, T t2) { Console::WriteLine("two"); 35 } generic<typename T>
static void F(T t1, int t2) { Console::WriteLine("three"); }
40
};
int main() {
X::F<int>(1); // explicit, prints "one"
X::F(1); // deduced, prints "one"
X::F<double>(5.0, 6.0); // explicit, prints "two"
45
X::F(5.0, 6.0); // deduced, prints "two"
X::F<double>(5.0, 3); // explicit, prints "three"
X::F(5.0, 3); // deduced, prints "three"
X::F<int>(1, 2); // error, ambiguous
X::F(1, 2); // error, ambiguous
50
X::F<double>(1, 2); // explicit, prints "three"
Generics ref class R : IX {}; generic<typename T> void f(T) {} void g(R^ hR) { f<IX^>(hR); // T is specified to be IX 5 f(hR); // T is deduced to be R } end example]
Type inference allows a more convenient syntax to be used for calling a generic function, and allows the programmer to avoid specifying redundant type information.
10
Type deduction within generics is handled like type deduction within templates (C++ Standard §14.8.2). If the generic function was declared with a parameter array, then type deduction is first performed against the function using its exact signature. If type deduction succeeds, and the resultant function is applicable, then the function is eligible for overload resolution in its normal form. Otherwise, type deduction is performed against the function in its expanded form (§18.3.6). The issue raised in 8.15.3 is somewhat 15
answered here. 18.3.6 seems to deal with expanded forms of calls, not expanded forms of function
declarations. I interpret the text above as saying that deduction is done as if the function were declared like this:
generic <typename ItemType>
void PushMultiple(Stack<ItemType>^, ItemType i1, ItemType i2,/* ... */); 20
Is that correct? I think this requires a more detailed description.[[#169]]
An instance of a delegate can be created that refers to a generic function declaration. The type arguments used when invoking a generic function through a delegate are determined when the delegate is instantiated. The type arguments can be given explicitly or be determined by type deduction. If type deduction is used, the parameter types of the delegate are used as argument types in the deduction process. The return type of 25
the delegate is not used for deduction. [Example: The following example shows both ways of supplying a type argument to a delegate instantiation expression:
delegate int D(String^ s, int i); delegate int E();
ref class X { 30 public: generic<typename T> static T F(String^ s, T t); generic<typename T> static T G(); 35 }; int main() {
D^ d1 = gcnew D(X::F<int>);// okay, type argument given explicitly
D^ d2 = gcnew D(X::F); // okay, int deduced as type argument
E^ e1 = gcnew E(X::G<int>);// okay, type argument given explicitly
40
E^ e2 = gcnew E(X::G); // error, cannot deduce from return type
}
end example]
A non-generic delegate type can be instantiated using a generic function. It is also possible to create an instance of a constructed delegate type using a generic function. In all cases, type arguments are given or 45
deduced when the delegate instance is created, and a type argument list shall not be supplied when that delegate is invoked.
30.4 Constraints
The set of type arguments that is permitted for any given type parameter in a generic type or function declaration can be restricted via the use of one or more constraints. Such constraints are specified via a constraint-clause-list: constraint-clause-list: 5 constraint-clause constraint-clause-list constraint-clause constraint-clause:
where identifier : constraint-item-list constaint-item-list: 10 constraint-item constraint-item-list , constraint-item constraint-item: type-id
Each constraint-clause consists of the token where, followed by an identifier that shall be the name of a 15
type parameter in the generic type declaration to which this constraint-clause applies, followed by a colon and the list of constraints for that type parameter. There shall be no more than one constraint-clause for each type parameter in any generic declaration, and the constraint-clauses can be listed in any order. The token
where is not a keyword.
A constraint-item-list can include any of the following constraint-items, in any order: a single class 20
constraint and one or more interface constraints (with each being specified via a type-id).
If a constraint-item is a class type or an interface type, that type specifies a minimal “base type” that every type argument used for that type parameter shall support. Whenever a constructed type or generic function is used, the type argument is checked against the constraints on the type parameter at compile-time. The type argument supplied shall derive from or implement all of the constraints given for that type parameter. 25
The type specified by type-id in a class constraint shall be a ref class type that is not sealed, and that type
shall not be any of the following: System::Array, System::Delegate, System::Enum, or
System::ValueType. A constraint-item-list shall contain no more than one constraint that is a class type.
The type specified by type-id in an interface constraint shall be an interface class type. The same interface type shall not be specified more than once in a given constraint-clause.
30
A class or interface constraint can involve any of the type parameters of the associated type or function declaration as part of a constructed type, and can involve the type being declared, but the constraint shall not be a type parameter alone.
Any class or interface type specified as a type parameter constraint shall be at least as accessible as the generic type or function being declared.
35
[Example: The following are examples of constraints:
generic<typename T>
interface class IComparable { int CompareTo(T value); };
40
generic<typename T>
interface class IKeyProvider {
T GetKey();
};
generic<typename T>
45
where T : IPrintable ref class Printer
Generics
generic<typename T>
where T : IComparable<T> ref class SortedList
{ … };
generic<typename K, typename V>
5
where K : IComparable<K>
where V : IPrintable, IKeyProvider<K> ref class Dictionary
{ … };
end example] 10
If a type parameter has no constraints associated with it then it is implicitly constrained by
System::Object. [Note: having a type parameter constrained in this manner severely limits what you can
do with the type within the body of the generic. end note]