Muokkaa

Jaa


Collections (C++/CX)

In a C++/CX program, you can make free use of Standard Template Library (STL) containers, or any other user-defined collection type. However, when you pass collections back and forth across the Windows Runtime application binary interface (ABI)—for example, to a XAML control or to a JavaScript client—you must use Windows Runtime collection types.

The Windows Runtime defines the interfaces for collections and related types, and C++/CX provides the concrete C++ implementations in the collection.h header file. This illustration shows the relationships between the collection types:

Diagram of C plus plus C X inheritance tree for collection types.

Vector usage

When your class has to pass a sequence container to another Windows Runtime component, use Windows::Foundation::Collections:: IVector<T> as the parameter or return type, and Platform::Collections::Vector<T> as the concrete implementation. If you attempt to use a Vector type in a public return value or parameter, compiler error C3986 will be raised. You can fix the error by changing the Vector to an IVector.

Important

If you are passing a sequence within your own program, then use either Vector or std::vector because they are more efficient than IVector. Use IVector only when you pass the container across the ABI.

The Windows Runtime type system does not support the concept of jagged arrays and therefore you cannot pass an IVector<Platform::Array<T>> as a return value or method parameter. To pass a jagged array or a sequence of sequences across the ABI, use IVector<IVector<T>^>.

Vector<T> provides the methods that are required for adding, removing, and accessing items in the collection, and it is implicitly convertible to IVector<T>. You can also use STL algorithms on instances of Vector<T>. The following example demonstrates some basic usage. The begin function and end function here are from the Platform::Collections namespace, not the std namespace.

#include <collection.h>
#include <algorithm>
using namespace Platform;
using namespace Platform::Collections;
using namespace Windows::Foundation::Collections;


void Class1::Test()
{
    Vector<int>^ vec = ref new Vector<int>();
    vec->Append(1);
    vec->Append(2);
    vec->Append(3);
    vec->Append(4);
    vec->Append(5);


    auto it = 
        std::find(begin(vec), end(vec), 3);

    int j = *it; //j = 3
    int k = *(it + 1); //or it[1]

    // Find a specified value.
    unsigned int n;         
    bool found = vec->IndexOf(4, &n); //n = 3

    // Get the value at the specified index.
    n = vec->GetAt(4); // n = 3

    // Insert an item.
    // vec = 0, 1, 2, 3, 4, 5
    vec->InsertAt(0, 0);

    // Modify an item.
    // vec = 0, 1, 2, 12, 4, 5,
    vec->SetAt(3, 12);

    // Remove an item.
    //vec = 1, 2, 12, 4, 5 
    vec->RemoveAt(0);

    // vec = 1, 2, 12, 4
    vec->RemoveAtEnd();

    // Get a read-only view into the vector.
    IVectorView<int>^ view = vec->GetView();
}

If you have existing code that uses std::vector and you want to reuse it in a Windows Runtime component, just use one of the Vector constructors that takes a std::vector or a pair of iterators to construct a Vector at the point where you pass the collection across the ABI. The following example shows how to use the Vector move constructor for efficient initialization from a std::vector. After the move operation, the original vec variable is no longer valid.

//#include <collection.h>
//#include <vector>
//#include <utility> //for std::move
//using namespace Platform::Collections;
//using namespace Windows::Foundation::Collections;
//using namespace std;
IVector<int>^ Class1::GetInts()
{
    vector<int> vec;
    for(int i = 0; i < 10; i++)
    {
        vec.push_back(i);
    }    
    // Implicit conversion to IVector
    return ref new Vector<int>(std::move(vec));
}

If you have a vector of strings that you must pass across the ABI at some future point, you must decide whether to create the strings initially as std::wstring types or as Platform::String^ types. If you have to do a lot of processing on the strings, then use wstring. Otherwise, create the strings as Platform::String^ types and avoid the cost of converting them later. You must also decide whether to put these strings into a std:vector or Platform::Collections::Vector internally. As a general practice, use std::vector and then create a Platform::Vector from it only when you pass the container across the ABI.

Value types in Vector

Any element to be stored in a Platform::Collections::Vector must support equality comparison, either implicitly or by using a custom std::equal_to comparator that you provide. All reference types and all scalar types implicitly support equality comparisons. For non-scalar value types such as Windows::Foundation::DateTime, or for custom comparisons—for example, objA->UniqueID == objB->UniqueID—you must provide a custom function object.

VectorProxy elements

Platform::Collections::VectorIterator and Platform::Collections::VectorViewIterator enable the use of range for loops and algorithms like std::sort with an IVector<T> container. But IVector elements cannot be accessed through C++ pointer dereference; they can be accessed only through GetAt and SetAt methods. Therefore, these iterators use the proxy classes Platform::Details::VectorProxy<T> and Platform::Details::ArrowProxy<T> to provide access to the individual elements through *, ->, and [] operators, as required by the Standard Library. Strictly speaking, given an IVector<Person^> vec, the type of *begin(vec) is VectorProxy<Person^>. However, the proxy object is almost always transparent to your code. These proxy objects are not documented because they are only for internal use by the iterators, but it is useful to know how the mechanism works.

When you use a range-based for loop over IVector containers, use auto&& to enable the iterator variable to bind correctly to the VectorProxy elements. If you use auto&, compiler warning C4239 is raised and VectoryProxy is mentioned in the warning text.

The following illustration shows a range for loop over an IVector<Person^>. Notice that execution is stopped on the breakpoint on line 64. The QuickWatch window shows that the iterator variable p is in fact a VectorProxy<Person^> that has m_v and m_i member variables. However, when you call GetType on this variable, it returns the identical type to the Person instance p2. The takeaway is that although VectorProxy and ArrowProxy might appear in QuickWatch, the debugger certain compiler errors, or other places, you typically don't have to explicitly code for them.

Screenshot of debugging VectorProxy in a range based for loop.

One scenario in which you have to code around the proxy object is when you have to perform a dynamic_cast on the elements—for example, when you are looking for XAML objects of a particular type in a UIElement element collection. In this case, you must first cast the element to Platform::Object^ and then perform the dynamic cast:

void FindButton(UIElementCollection^ col)
{
    // Use auto&& to avoid warning C4239
    for (auto&& elem : col)
    {
        Button^ temp = dynamic_cast<Button^>(static_cast<Object^>(elem));
        if (nullptr != temp)
        {
            // Use temp...
        }
    }
}

Map usage

This example shows how to insert items and look them up in a Platform::Collections::Map, and then return the Map as a read-only Windows::Foundation::Collections::IMapView type.

//#include <collection.h>
//using namespace Platform::Collections;
//using namespace Windows::Foundation::Collections;
IMapView<String^, int>^ Class1::MapTest()
{
    Map<String^, int>^ m = ref new Map<String^, int >();
    m->Insert("Mike", 0);
    m->Insert("Dave", 1);
    m->Insert("Doug", 2);
    m->Insert("Nikki", 3);
    m->Insert("Kayley", 4);
    m->Insert("Alex", 5);
    m->Insert("Spencer", 6);

   // PC::Map does not support [] operator
   int i = m->Lookup("Doug");
   
   return m->GetView();
   
}

In general, for internal map functionality, prefer the std::map type for performance reasons. If you have to pass the container across the ABI, construct a Platform::Collections::Map from the std::map and return the Map as an Windows::Foundation::Collections::IMap. If you attempt to use a Map type in a public return value or parameter, compiler error C3986 will be raised. You can fix the error by changing the Map to an IMap. In some cases—for example, if you are not making a large number of lookups or insertions, and you are passing the collection across the ABI frequently—it might be less expensive to use Platform::Collections::Map from the beginning and avoid the cost of converting the std::map. In any case, avoid lookup and insert operations on an IMap because these are the least performant of the three types. Convert to IMap only at the point that you pass the container across the ABI.

Value types in Map

Elements in a Platform::Collections::Map are ordered. Any element to be stored in a Map must support less-than comparison with strict weak ordering, either implicitly or by using a custom stl::less comparator that you provide. Scalar types support the comparison implicitly. For non-scalar value types such as Windows::Foundation::DateTime, or for custom comparisons—for example, objA->UniqueID < objB->UniqueID—you must provide a custom comparator.

Collection types

Collections fall into four categories: modifiable versions and read-only versions of sequence collections and associative collections. In addition, C++/CX enhances collections by providing three iterator classes that simplify the accessing of collections.

Elements of a modifiable collection can be changed, but elements of a read-only collection, which is known as a view, can only be read. Elements of a Platform::Collections::Vector orPlatform::Collections::VectorView collection can be accessed by using an iterator or the collection's Vector::GetAt and an index. Elements of an associative collection can be accessed by using the collection's Map::Lookup and a key.

Platform::Collections::Map class
A modifiable, associative collection. Map elements are key-value pairs. Looking up a key to retrieve its associated value, and iterating through all key-value pairs, are both supported.

Map and MapView are templated on <K, V, C = std::less<K>>; therefore, you can customize the comparator. Additionally, Vector and VectorView are templated on <T, E = std::equal_to<T>> so that you can customize the behavior of IndexOf(). This is important mostly for Vector and VectorView of value structs. For example, to create a Vector<Windows::Foundation::DateTime>, you must provide a custom comparator because DateTime does not overload the == operator.

Platform::Collections::MapView class
A read-only version of a Map.

Platform::Collections::Vector class
A modifiable sequence collection. Vector<T> supports constant-time random access and amortized-constant-time Append operations..

Platform::Collections::VectorView class
A read-only version of a Vector.

Platform::Collections::InputIterator class
An STL iterator that satisfies the requirements of an STL input iterator.

Platform::Collections::VectorIterator class
An STL iterator that satisfies the requirements of an STL mutable random-access iterator.

Platform::Collections::VectorViewIterator class
An STL iterator that satisfies the requirements of an STL const random-access iterator.

begin() and end() functions

To simplify the use of the STL to process Vector, VectorView, Map, MapView, and arbitrary Windows::Foundation::Collections objects, C++/CX supports overloads of the begin Function and end Function non-member functions.

The following table lists the available iterators and functions.

Iterators Functions
Platform::Collections::VectorIterator<T>

(Internally stores Windows::Foundation::Collections:: IVector<T> and int.)
begin/ end(Windows::Foundation::Collections:: IVector<T>)
Platform::Collections::VectorViewIterator<T>

(Internally stores IVectorView<T>^ and int.)
begin/ end (IVectorView<T>^)
Platform::Collections::InputIterator<T>

(Internally stores IIterator<T>^ and T.)
begin/ end (IIterable<T>)
Platform::Collections::InputIterator<IKeyValuePair<K, V>^>

(Internally stores IIterator<T>^ and T.)
begin/ end (IMap<K,V>.
Platform::Collections::InputIterator<IKeyValuePair<K, V>^>

(Internally stores IIterator<T>^ and T.)
begin/ end (Windows::Foundation::Collections::IMapView)

Collection change events

Vector and Map support databinding in XAML collections by implementing events that occur when a collection object is changed or reset, or when any element of a collection is inserted, removed, or changed. You can write your own types that support databinding, although you cannot inherit from Map or Vector because those types are sealed.

The Windows::Foundation::Collections::VectorChangedEventHandler and Windows::Foundation::Collections::MapChangedEventHandler delegates specify the signatures for event handlers for collection change events. The Windows::Foundation::Collections::CollectionChange public enum class, and Platform::Collection::Details::MapChangedEventArgs and Platform::Collections::Details::VectorChangedEventArgs ref classes, store the event arguments to determine what caused the event. The *EventArgs types are defined in the Details namespace because you don't have to construct or consume them explicitly when you use Map or Vector.

See also

Type System
C++/CX Language Reference
Namespaces Reference