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:
The Platform::Collections::Vector class resembles the std::vector class.
The Platform::Collections::Map class resembles the std::map class.
Platform::Collections::VectorView class andPlatform::Collections::MapView class are read-only versions of
Vector
andMap
.Iterators are defined in the Platform::Collections Namespace. These iterators satisfy the requirements for STL iterators and enable the use of std::find, std::count_if, and other STL algorithms on any Windows::Foundation::Collections interface type or Platform::Collections concrete type. For example, this means that you can iterate a collection in a Windows Runtime component that's created in C# and apply an STL algorithm to it.
Important
Proxy iterators
VectorIterator
andVectorViewIterator
utilize proxy objectsVectoryProxy<T>
andArrowProxy<T>
to enable usage with STL containers. For more information, see "VectorProxy elements" later in this article.The C++/CX collection types support the same thread safety guarantees that STL containers support.
Windows::Foundation::Collections::IObservableVector and Windows::Foundation::Collections::IObservableMap define events that are fired when the collection changes in various ways. By implementing these interfaces, Platform::Collections::Map and Platform::Collections::Vector support databinding with XAML collections. For example, if you have a
Vector
that is data-bound to aGrid
, when you add an item to a collection, the change is reflected in the Grid UI.
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.
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
.