Generic C++ Properties

Discussion in 'C++' started by Sanskruti, Mar 19, 2007.

  1. Sanskruti

    Sanskruti New Member

    Joined:
    Jan 7, 2007
    Messages:
    108
    Likes Received:
    18
    Trophy Points:
    0
    Occupation:
    Software Consultant
    Location:
    Mumbai, India
    Generics are parameterized types supported by the common language runtime. A parameterized type is a type that is defined with an unknown type parameter that is specified when the generic is used.

    C++ supports templates and both templates and generics support parameterized types to create typed collection classes. However, templates provide compile-time parameterization. You cannot reference an assembly containing a template definition and create new specializations of the template.

    Once compiled, a specialized template looks like any other class or method. In contrast, generics are emitted in MSIL as a parameterized type known by the runtime to be a parameterized type; source code that references an assembly containing a generic type can create specializations of the generic type.

    Class types, as long as they are managed types, may be generic. The type of object in the list would be the type parameter. If you needed a List class for many different types of objects, before generics you might have used a List that takes System::Object as the item type.

    But that would allow any object to be used in the list. Such a list would be called an untyped collection class. You could check the type at runtime and throw an exception. Or, you might have used a template, which would lose its generic quality once compiled into an assembly. Consumers of your assembly could not create their own specializations of the template. Generics allow you to create typed collection classes, say List<int> and List<double> ("List of double") which would generate a compile-time error if you tried to put a type that the collection was not designed to accept into the typed collection.

    Type Parameters



    A generic declaration contains one or more unknown types known as type parameters. Type parameters are given a name which stands for the type within the body of the generic declaration. The type parameter is used as a type within the body of the generic declaration. The generic declaration for List<T> contains the type parameter T.

    Type Arguments



    The type argument is the actual type used in place of the type parameter when the generic is specialized for a specific type or types. For example, int is the type argument in List<int>. Value types and handle types are the only types allowed in as a generic type argument.

    Constructed Type



    A type constructed from a generic type is referred to as a constructed type. A type not fully specified, such as List<T> is an open constructed type; a type fully specified, such as List<double>, is a closed constructed type or specialized type. Open constructed types may be used in the definition of other generic types or methods and may not be fully specified until the enclosing generic is itself specified.

    Reference Types and Value Types



    Handles types and value types may be used as type arguments. In the generic definition, in which either type may be used, the syntax is that of reference types.
    For example, the -> operator is used to access members of the type of the type parameter whether or not the type eventually used is a reference type or a value type. When a value type is used as the type argument, the runtime generates code that uses the value types directly without boxing the value types.

    Type Parameters



    Type parameters in a generic class are treated like other identifiers. However, because the type is not known, there are restrictions on their use. For example, you cannot use members and methods of the type parameter class unless the type parameter is known to support these members. That is, in order access a member through the type parameter, you must add the type that contains the member to the type parameter's constraint list.

    Code:
    interface class I 
    { 
    	void f1(); 
    	void f2(); 
    }; 
    
    ref struct R : public I 
    { 
    	virtual void f1() {} 
    	virtual void f2() {} 
    	virtual void f3() {} 
    };
    
    generic <typename T> 
    where T : I 
    void f(T t) 
    { 
    	t->f1(); 
    	t->f2(); 
    	safe_cast<R^>(t)->f3(); 
    }
     int main() 
    { 
    	f(gcnew R()); 
    }
    These restrictions apply to operators as well. An unconstrained generic type parameter may not use the == and != operators to compare two instances of the type parameter, in case the type does not support these operators. These checks are necessary for generics, but not for templates, because generics may be specialized at runtime with any class that satisfies the constraints, when it is too late to check for the use of invalid members.

    A default instance of the type parameter may be created by using the () operator.
    For example:
    T t = T();
    where T is a type parameter in a generic class or method definition, initializes the variable to its default value. If T is a ref class it will be a null pointer; if T is a value class, the object is initialized to zero. This is called a default initializer.

    Generic Functions



    Generic functions may be necessary if the function's parameters are of an unknown type, or if the function itself must work with generic types. In many cases where System::Object may have been used in the past as a parameter for an unknown object type, a generic type parameter may be used instead, allowing for more type-safe code. Any attempt to pass in a type that the function was not designed for would be flagged as an error at compile time. Using System::Object as a function parameter, the inadvertent passing of an object that the function wasn't intended to deal with would not be detected, and you would have to cast the unknown object type to a specific type in the function body, and account for the possibility of an InvalidCastException. With a generic, code attempting to pass an object to the function would cause a type conflict so the function body is guaranteed to have the correct type.

    A generic function is a function that is declared with type parameters.
    When called, actual types are used instead of the type parameters.

    Code:
    [attributes] [modifiers]
     return-type identifier <type-parameter identifier(s)> 
    [type-parameter-constraints clauses] 
    ([formal-parameters])
     {
     function-body 
    }
    The above syntax includes these terms, defined as follows:

    attributes (Optional)
    Additional declarative information. ​
    modifiers (Optional)
    A modifier for the function, such as static. virtual is not allowed since virtual methods may not be generic.​
    return-type
    The type returned by the method. If the return type is void, no return value is required.​
    identifier
    The function name.​
    type-parameter identifier(s)
    Comma-separated identifiers list.​
    formal-parameters (Optional)
    Parameter list.​
    type-parameter-constraints-clauses
    This specifies restrictions on the types that may be used as type arguments, and takes the form specified in Constraints.​
    function-body
    The body of the method, which may refer to the type parameter identifiers.​
    Generic functions are functions declared with a generic type parameter. They may be methods in a class or struct, or standalone functions. A single generic declaration implicitly declares a family of functions that differ only in the substitution of a different actual type for the generic type parameter.

    When called, the generic type parameter is replaced by an actual type. The actual type may be explicitly specified in angled brackets using syntax similar to a template function call. If called without the type parameters, the compiler will attempt to deduce the actual type from the parameters supplied in the function call. If the intended type argument cannot be deduced from the parameters used, the compiler will report an error.

    An Example

    Code:
    generic <typename ItemType> 
    void G(int i) {}
     ref struct A { 
    generic <typename ItemType> 
    void G(ItemType) {} 
    generic <typename ItemType> 
    static void H(int i) {} 
    }; 
    int main() 
    { 
    	A myObject; 
    	// generic function call 
    	myObject.G<int>(10); 
    	// generic function call with type parameters deduced myObject.G(10); 
    	// static generic function call 
    	A::H<int>(10); 
    	// global generic function call
    	G<int>(10);
    }
    Generic functions can be overloaded based on signature, the number of type parameters on a function.. Also, generic functions can be overloaded with non-generic functions of the same name, as long as the functions differ in some type parameters.

    For example, the following functions can be overloaded:
    Code:
    ref struct MyClass { 
    	void MyMethod(int i) {} 
    
    	generic <class T> 
    	void MyMethod(int i) {}
     
    	generic <class T, class V> 
    	void MyMethod(int i) {} 
    };

    Generic Classes



    Collection classes in the past would use System::Object to store elements in a collection. Insertion of objects of a type that the collection was not designed for was not flagged at compile time, and often not even when the objects were inserted. Usually, an object would be cast to some other type when it was accessed in the collection. Only when the cast failed would the unexpected type be detected. Generics solves this problem at compile time by detecting any code that inserts a type that doesn't match (or implicitly convert to) the type parameter of the generic collection

    A generic class is declared using the following form:

    Code:
    [attributes] 
    generic <class-key type-parameter-identifier(s)>
    [constraint-clauses] 
    [accessibility-modifiers] 
    ref class identifier [modifiers] 
    [: base-list] 
    { 
    	class-body 
    } 
    [declarators] [;]
    In the above syntax, the following terms are used:

    attributes (optional)
    Additional declarative information. ​
    class-key
    Either class or typename​
    type-parameter-identifier(s)
    Comma-separated list of identifiers specifying the names of the type parameters.​
    constraint-clauses
    A list (not comma-separated) of where clauses specifying the constraints for the type parameters. Takes the form: where type-parameter-identifier : constraint-list ...​
    constraint-list
    class-or-interface[, ...] ​
    accessibility-modifiers
    Allowed accessibility modifiers include public and private.​
    identifier
    The name of the generic class, any valid C++ identifier.​
    modifiers (optional)
    Allowed modifiers include sealed and abstract.​
    base-list
    A list that contains the one base class and any implemented interfaces, all separated by commas.​
    class-body
    The body of the class, containing fields, member functions, etc.​
    declarators
    Declarations of any variables of this type. For example: ^identifier[, ...]
    You can declare generic classes such as these (note that the keyword class may be used instead of typename). ​
    Code:
    using namespace System; 
    generic <typename ItemType> 
    ref struct Stack { 
    // ItemType may be used as a type here
     void Add(ItemType item) {} 
    }; 
    
    generic <typename KeyType, typename ValueType> 
    ref class HashTable {}; 
    
    // The keyword class may be used instead of typename: 
    generic <class ListItem>
     ref class List {}; 
    int main() 
    { 
    HashTable<int, Decimal>^ g1 = gcnew HashTable<int, Decimal>(); 
    }
    Both value types (either built-in types such as int or double, or user-defined value types) and reference types may be used as a generic type argument. The syntax within the generic definition is the same regardless. Syntactically, the unknown type is treated as if it were a reference type. However, the runtime is able to determine that if the type actually used is a value type and substitute the appropriate generated code for direct access to members. Value types used as generic type arguments are not boxed and so do not suffer the performance penalty associated with boxing. The syntax used within the body of the generic should be T^ and '->' instead of '.'.

    C++ supports templates and both templates and generics support parameterized types to create typed collection classes. However, templates provide compile-time parameterization. You cannot reference an assembly containing a template definition and create new specializations of the template. Once compiled, a specialized template looks like any other class or method. In contrast, generics are emitted in MSIL as a parameterized type known by the runtime to be a parameterized type; source code that references an assembly containing a generic type can create specializations of the generic type.
     
  2. rashida.par

    rashida.par New Member

    Joined:
    Feb 14, 2008
    Messages:
    21
    Likes Received:
    1
    Trophy Points:
    0

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice