C++/WinRT の強参照と弱参照Strong and weak references in C++/WinRT

Windows ランタイムは参照カウント システムです。このようなシステムでは、強参照と弱参照 (および、暗黙的 this ポインターのように、いずれでもない参照) の重要性とこれらの違いを認識することが重要です。The Windows Runtime is a reference-counted system; and in such a system it's important for you to know about the significance of, and distinction between, strong and weak references (and references that are neither, such as the implicit this pointer). このトピックで説明しますが、このような参照を正しく扱う方法を知ることは、円滑に動作する安定したシステムと突然クラッシュするシステムの違いを意味することがあります。As you'll see in this topic, knowing how to manage these references correctly can mean the difference between a reliable system that runs smoothly, and one that crashes unpredictably. C++/WinRT により言語プロジェクションを広範囲で支えるヘルパー関数が提供され、複雑なシステムを簡単かつ正しく構築する作業が半分まで片付きます。By providing helper functions that have deep support in the language projection, C++/WinRT meets you halfway in your work of building more complex systems simply and correctly.

class-member コルーチンで this ポインターに安全にアクセスするSafely accessing the this pointer in a class-member coroutine

コルーチンの詳細とコード例については、「C++/WinRT を使用した同時実行操作と非同期操作」を参照してください。For more info about coroutines, and code examples, see Concurrency and asynchronous operations with C++/WinRT.

下のコード一覧では、あるクラスのメンバー関数であるコルーチンの典型的な例を確認できます。The code listing below shows a typical example of a coroutine that's a member function of a class. 新しい Windows コンソール アプリケーション (C++/WinRT) プロジェクトで、この例をコピーし、指定のファイルに貼り付けることができます。You can copy-paste this example into the specified files in a new Windows Console Application (C++/WinRT) project.

// pch.h
#pragma once
#include <iostream>
#include <winrt/Windows.Foundation.h>

// main.cpp : Defines the entry point for the console application.
#include "pch.h"

using namespace winrt;
using namespace Windows::Foundation;
using namespace std::chrono_literals;

struct MyClass : winrt::implements<MyClass, IInspectable>
{
    winrt::hstring m_value{ L"Hello, World!" };

    IAsyncOperation<winrt::hstring> RetrieveValueAsync()
    {
        co_await 5s;
        co_return m_value;
    }
};

int main()
{
    winrt::init_apartment();

    auto myclass_instance{ winrt::make_self<MyClass>() };
    auto async{ myclass_instance->RetrieveValueAsync() };

    winrt::hstring result{ async.get() };
    std::wcout << result.c_str() << std::endl;
}

MyClass::RetrieveValueAsync はしばらくの間動作し、最終的に MyClass::m_value データ メンバーのコピーを返します。MyClass::RetrieveValueAsync spends some time working, and eventually it returns a copy of the MyClass::m_value data member. RetrieveValueAsync を呼び出すと、非同期オブジェクトが作成されます。そのオブジェクトには、暗黙的 this ポインターが含まれます (これを経由し、最終的に m_value にアクセスします)。Calling RetrieveValueAsync causes an asynchronous object to be created, and that object has an implicit this pointer (through which, eventually, m_value is accessed).

コルーチンでは、最初の一時停止ポイントまで実行は同期であり、そこで呼び出し元に制御が返されることを思い出してください。Remember that, in a coroutine, execution is synchronous up until the first suspension point, where control is returned to the caller. RetrieveValueAsync では、最初の co_await は最初の中断ポイントです。In RetrieveValueAsync, the first co_await is the first suspension point. コルーチンが再開されるまで (この場合は約 5 秒後)、m_value にアクセスするために使用している暗黙の this ポインターに対して何かが発生している可能性があります。By the time the coroutine resumes (around five seconds later, in this case), anything might have happened to the implicit this pointer through which we access m_value.

イベントは次のように進行します。Here's the full sequence of events.

  1. main で、MyClass のインスタンスが作成されます (myclass_instance)。In main, an instance of MyClass is created (myclass_instance).
  2. async オブジェクトが作成されます。これは (その this を介して) myclass_instance を指します。The async object is created, pointing (via its this) to myclass_instance.
  3. winrt::Windows::Foundation::IAsyncAction::get 関数では、その最初の中断ポイントに達し、数秒間ブロックしてから、RetrieveValueAsync の結果が返されます。The winrt::Windows::Foundation::IAsyncAction::get function hits its first suspension point, blocks for a few seconds, and then returns the result of RetrieveValueAsync.
  4. RetrieveValueAsync から値 this->m_value が返されます。RetrieveValueAsync returns the value of this->m_value.

手順 4 は、this が有効である間だけ安全です。Step 4 is safe only as long as this remains valid.

しかし、非同期操作の完了前にクラス インスタンスが破棄された場合はどうなるでしょうか。But what if the class instance is destroyed before the async operation completes? 非同期メソッドの完了前にクラス インスタンスが範囲から外れることは、あらゆる形でありえます。There are all kinds of ways the class instance could go out of scope before the asynchronous method has completed. しかし、クラス インスタンスを nullptr に設定することでそれをシミュレートできます。But we can simulate it by setting the class instance to nullptr.

int main()
{
    winrt::init_apartment();

    auto myclass_instance{ winrt::make_self<MyClass>() };
    auto async{ myclass_instance->RetrieveValueAsync() };
    myclass_instance = nullptr; // Simulate the class instance going out of scope.

    winrt::hstring result{ async.get() }; // Behavior is now undefined; crashing is likely.
    std::wcout << result.c_str() << std::endl;
}

クラス インスタンスを破棄する箇所を過ぎると、それを再び直接参照することはないようです。After the point where we destroy the class instance, it looks like we don't directly refer to it again. しかし、当然、この非同期オブジェクトにはそれを指す this ポインターが与えられており、それを使用し、クラス インスタンス内に格納されている値をコピーしようとします。But of course the asynchronous object has a this pointer to it, and tries to use that to copy the value stored inside the class instance. このコルーチンはメンバー関数であり、その this ポインターを問題なく使用できるはずです。The coroutine is a member function, and it expects to be able to use its this pointer with impunity.

このようにコードを変更することで、手順 4 の問題に遭遇します。クラス インスタンスが破棄されており、this が無効になっているためです。With this change to the code, we run into a problem in step 4, because the class instance has been destroyed, and this is no longer valid. 非同期オブジェクトがクラス インスタンス内の変数にアクセスしようとすると、直後にクラッシュします (あるいは、まったく未定義の何かが起こります)。As soon as the asynchronous object attempts to access the variable inside the class instance, it will crash (or do something entirely undefined).

この解決策は、—非同期操作 (コルーチン)— にクラス インスタンスの強参照を与えることです。The solution is to give the asynchronous operation—the coroutine—its own strong reference to the class instance. 現時点では、コルーチンは実際にクラス インスタンスの生の this ポインターを保持していますが、それだけではクラス インスタンスを維持できません。As currently written, the coroutine effectively holds a raw this pointer to the class instance; but that's not enough to keep the class instance alive.

クラス インスタンスを維持するには、RetrieveValueAsync の実装を下のように変更します。To keep the class instance alive, change the implementation of RetrieveValueAsync to that shown below.

IAsyncOperation<winrt::hstring> RetrieveValueAsync()
{
    auto strong_this{ get_strong() }; // Keep *this* alive.
    co_await 5s;
    co_return m_value;
}

C++/WinRT クラスは、winrt::implements テンプレートから直接的または間接的に派生します。A C++/WinRT class directly or indirectly derives from the winrt::implements template. そのため、C++/WinRT オブジェクトでは、implements.get_strong というその保護メンバー関数を呼び出し、その this ポインターの強参照を取得できます。Because of that, the C++/WinRT object can call its implements.get_strong protected member function to retrieve a strong reference to its this pointer. 上のコード例では実際に strong_this 変数を使用する必要はないことにご留意ください。get_strong を呼び出すだけで、C++/WinRT オブジェクトの参照カウントがインクリメントされ、その暗黙的 this ポインターの有効な状態が維持されます。Note that there's no need to actually use the strong_this variable in the code example above; simply calling get_strong increments the C++/WinRT object's reference count, and keeps its implicit this pointer valid.

重要

get_strongwinrt::implements 構造体テンプレートのメンバー関数であるため、C++/WinRT クラスなど、winrt::implements から直接的または間接的に派生するクラスからのみ呼び出すことができます。Because get_strong is a member function of the winrt::implements struct template, you can call it only from a class that directly or indirectly derives from winrt::implements, such as a C++/WinRT class. winrt::implements から派生されるものに関する詳細と例については、「C++/WinRT での API の作成」を参照してください。For more info about deriving from winrt::implements, and examples, see Author APIs with C++/WinRT.

これで以前、手順 4 に進んだときに発生した問題が解決されます。This resolves the problem that we previously had when we got to step 4. クラス インスタンスの他の参照がすべて消えても、その依存関係を安定させるという予防策がコルーチンでとられます。Even if all other references to the class instance disappear, the coroutine has taken the precaution of guaranteeing that its dependencies are stable.

強参照が適切でない場合、代わりに implements::get_weak を呼び出し、this の弱参照を取得できます。If a strong reference isn't appropriate, then you can instead call implements::get_weak to retrieve a weak reference to this. this にアクセスする前に強参照を取得できることを確認してください。Just confirm that you can retrieve a strong reference before accessing this. 繰り返しになりますが、get_weakwinrt::implements 構造体テンプレートのメンバー関数です。Again, get_weak is a member function of the winrt::implements struct template.

IAsyncOperation<winrt::hstring> RetrieveValueAsync()
{
    auto weak_this{ get_weak() }; // Maybe keep *this* alive.

    co_await 5s;

    if (auto strong_this{ weak_this.get() })
    {
        co_return m_value;
    }
    else
    {
        co_return L"";
    }
}

上の例では、強参照が残っていないとき、弱参照ではクラス インスタンスの破棄が止められません。In the example above, the weak reference doesn't keep the class instance from being destroyed when no strong references remain. しかし、メンバー変数にアクセスする前に、強参照を取得できるかどうかを確認する方法が、これにより与えられます。But it gives you a way of checking whether a strong reference can be acquired before accessing the member variable.

イベント処理デリゲートで this ポインターに安全にアクセスするSafely accessing the this pointer with an event-handling delegate

シナリオThe scenario

イベント処理に関する一般情報については、「C++/WinRT でのデリゲートを使用したイベントの処理」を参照してください。For general info about event-handling, see Handle events by using delegates in C++/WinRT.

前のセクションでは、コルーチンと同時開催性の領域における潜在的な有効期間の問題を取り上げました。The previous section highlighted potential lifetime issues in the areas of coroutines and concurrency. しかし、オブジェクトのメンバー関数でイベントを処理する場合、あるいはオブジェクトのメンバー関数内にあるラムダ関数内からイベントを処理する場合、イベント受信側 (イベントを処理するオブジェクト) とイベント ソース (イベントを発生させるオブジェクト) の相対的な有効期間を考慮する必要があります。But, if you handle an event with an object's member function, or from within a lambda function inside an object's member function, then you need to think about the relative lifetimes of the event recipient (the object handling the event) and the event source (the object raising the event). コード例をいくつか見てみましょう。Let's look at some code examples.

下のコードでは、まず、簡単な EventSource クラスが定義されます。これから発生する汎用イベントは、それに追加されているデリゲートで処理されます。The code listing below first defines a simple EventSource class, which raises a generic event that's handled by any delegates that have been added to it. この例のイベントではデリゲート タイプとしてたまたま Windows::Foundation::EventHandler が使用されていますが、ここで紹介する問題と解決策はあらゆるデリゲート タイプに適用されます。This example event happens to use the Windows::Foundation::EventHandler delegate type, but the issues and remedies here apply to any and all delegate types.

次に、EventRecipient クラスからラムダ関数の形式で EventSource::Event イベントのハンドラーが与えられます。Then, the EventRecipient class provides a handler for the EventSource::Event event in the form of a lambda function.

// pch.h
#pragma once
#include <iostream>
#include <winrt/Windows.Foundation.h>

// main.cpp : Defines the entry point for the console application.
#include "pch.h"

using namespace winrt;
using namespace Windows::Foundation;

struct EventSource
{
    winrt::event<EventHandler<int>> m_event;

    void Event(EventHandler<int> const& handler)
    {
        m_event.add(handler);
    }

    void RaiseEvent()
    {
        m_event(nullptr, 0);
    }
};

struct EventRecipient : winrt::implements<EventRecipient, IInspectable>
{
    winrt::hstring m_value{ L"Hello, World!" };

    void Register(EventSource& event_source)
    {
        event_source.Event([&](auto&& ...)
        {
            std::wcout << m_value.c_str() << std::endl;
        });
    }
};

int main()
{
    winrt::init_apartment();

    EventSource event_source;
    auto event_recipient{ winrt::make_self<EventRecipient>() };
    event_recipient->Register(event_source);
    event_source.RaiseEvent();
}

パターンは、その this ポインターに依存するラムダ イベント ハンドラーがイベントの受信側に与えられるというものです。The pattern is that the event recipient has a lambda event handler with dependencies on its this pointer. イベントの受信側がイベント ソースより長く残るときは必ず、その依存関係よりも長く残ります。Whenever the event recipient outlives the event source, it outlives those dependencies. その場合、共通して、このパターンが問題なく機能します。And in those cases, which are common, the pattern works well. UI ページでそのページ上にあるコントロールで発生したイベントを処理するときなど、明らかな場合があります。Some of these cases are obvious, such as when a UI page handles an event raised by a control that's on the page. ページの有効期間がボタンより長いため、ハンドラーの有効期間もボタンより長くなります。The page outlives the button—so, the handler also outlives the button. これは、受信側がソースを所有する場合 (データ メンバーとしてなど)、または受信側とソースが兄弟関係にあり、他のオブジェクトによって直接所有されている場合に当てはまります。This holds true any time the recipient owns the source (as a data member, for example), or any time the recipient and the source are siblings and directly owned by some other object.

ハンドラーの有効期間が、ハンドラーが依存する this より長くない場合があることが確実なときは、有効期間の強弱を気にしなくても、通常どおり this をキャプチャできます。When you're sure you have a case where the handler won't outlive the this that it depends on, then you can capture this normally, without consideration for strong or weak lifetime.

ただし、this がハンドラー内の自身の用途に表示されない場合などもあります (非同期アクションと非同期操作によって発生する完了イベントと進行状況イベントのハンドラーなど)。それに対処する方法を知ることは重要です。But there are still cases where this doesn't outlive its use in a handler (including handlers for completion and progress events raised by asynchronous actions and operations), and it's important to know how to deal with them.

  • イベント ソースでそのイベントが同期的に生成される場合は、ハンドラーを取り消して、それ以上イベントを受け取ることはないという確信を持つことができます。When an event source raises its events synchronously, you can revoke your handler and be confident that you won't receive any more events. ただし、非同期イベントの場合は、取り消し後 (特にデストラクター内で取り消す場合) でも、破棄が開始された後に実行中のイベントがオブジェクトに到着する可能性があります。But for asynchronous events, even after revoking (and especially when revoking within the destructor), an in-flight event might reach your object after it has started destructing. 破棄の前に登録を解除する場所を見つけることで問題を軽減できますが、引き続き堅牢なソリューションについて判断してください。Finding a place to unsubscribe prior to destruction might mitigate the issue, but continue reading for a robust solution.
  • 非同期メソッドを実装するためにコルーチンを作成する場合は可能です。If you're authoring a coroutine to implement an asynchronous method, then it's possible.
  • 特定の XAML UI フレームワーク オブジェクト (SwapChainPanel など) を使用するまれなケースで、イベント ソースから登録解除しなくても、受信側が最終処理される場合は可能です。In rare cases with certain XAML UI framework objects (SwapChainPanel, for example), then it's possible, if the recipient is finalized without unregistering from the event source.

問題The issue

次に紹介する main 関数では、イベント ソースで依然としてイベントを発生させているとき、イベントの受信側が破棄された場合 (おそらく範囲の外に出た場合) どうなるかをシミュレートします。This next version of the main function simulates what happens when the event recipient is destroyed (perhaps it goes out of scope) while the event source is still raising events.

int main()
{
    winrt::init_apartment();

    EventSource event_source;
    auto event_recipient{ winrt::make_self<EventRecipient>() };
    event_recipient->Register(event_source);
    event_recipient = nullptr; // Simulate the event recipient going out of scope.
    event_source.RaiseEvent(); // Behavior is now undefined within the lambda event handler; crashing is likely.
}

イベントの受信側は破棄されますが、その中のラムダ イベント ハンドラーは依然として Event イベントにサブスクライブされています。The event recipient is destroyed, but the lambda event handler within it is still subscribed to the Event event. そのイベントが発生すると、ラムダは、その時点では無効になっている this ポインターを逆参照しようとします。When that event is raised, the lambda attempts to dereference the this pointer, which is at that point invalid. そのため、それを使用しようとするハンドラー (または、コルーチンの継続) のコードからアクセス違反が出ます。So, an access violation results from code in the handler (or in a coroutine's continuation) attempting to use it.

重要

このような状況になった場合は、this オブジェクトの有効期間と、キャプチャした this オブジェクトがキャプチャの有効期間を超えて存続するかどうかについて考えます。If you encounter a situation like this, then you'll need to think about the lifetime of the this object; and whether or not the captured this object outlives the capture. キャプチャの有効期間を超えることがなければ、下に示すように、強参照または弱参照でキャプチャします。If it doesn't, then capture it with a strong or a weak reference, as we'll demonstrate below.

あるいは、これがご自分のシナリオに適していて、スレッドの考慮事項にも対応する場合は、受信側でイベントが完了した後、あるいは受信側のデストラクターでハンドラーを取り消すという選択肢があります。Or—if it makes sense for your scenario, and if threading considerations make it even possible—then another option is to revoke the handler after the recipient is done with the event, or in the recipient's destructor. 登録済みデリゲートの取り消し」を参照してください。See Revoke a registered delegate.

ハンドラーは次のように登録します。This is how we're registering the handler.

event_source.Event([&](auto&& ...)
{
    std::wcout << m_value.c_str() << std::endl;
});

このラムダでは、参照によってあらゆるローカル変数が自動的にキャプチャされます。The lambda automatically captures any local variables by reference. そのため、この例の場合、次のように記述しても同じことになります。So, for this example, we could equivalently have written this.

event_source.Event([this](auto&& ...)
{
    std::wcout << m_value.c_str() << std::endl;
});

いずれの場合でも、生の this ポインターをキャプチャするだけです。In both cases, we're just capturing the raw this pointer. それは参照カウントに何の影響も与えません。そのため、現在のオブジェクトの破棄を止めるものはありません。And that has no effect on reference-counting, so nothing is preventing the current object from being destroyed.

解決策The solution

解決策は、強参照 (または、ここで説明するように、より適切な場合は弱参照) をキャプチャすることです。The solution is to capture a strong reference (or, as we'll see, a weak reference if that's more appropriate). 強参照は参照カウントをインクリメントします。また、現在のオブジェクトの有効な状態を維持しますA strong reference does increment the reference count, and it does keep the current object alive. キャプチャ変数 (この例では strong_this) を宣言し、implements.get_strong を呼び出すことでそれを初期化します。結果、this ポインターの強参照が取得されます。You just declare a capture variable (called strong_this in this example), and initialize it with a call to implements.get_strong, which retrieves a strong reference to our this pointer.

重要

get_strongwinrt::implements 構造体テンプレートのメンバー関数であるため、C++/WinRT クラスなど、winrt::implements から直接的または間接的に派生するクラスからのみ呼び出すことができます。Because get_strong is a member function of the winrt::implements struct template, you can call it only from a class that directly or indirectly derives from winrt::implements, such as a C++/WinRT class. winrt::implements から派生されるものに関する詳細と例については、「C++/WinRT での API の作成」を参照してください。For more info about deriving from winrt::implements, and examples, see Author APIs with C++/WinRT.

event_source.Event([this, strong_this { get_strong()}](auto&& ...)
{
    std::wcout << m_value.c_str() << std::endl;
});

現在のオブジェクトの自動キャプチャを省略し、暗黙的 this を経由する代わりに、キャプチャ変数を介してデータ メンバーにアクセスすることもできます。You can even omit the automatic capture of the current object, and access the data member through the capture variable instead of via the implicit this.

event_source.Event([strong_this { get_strong()}](auto&& ...)
{
    std::wcout << strong_this->m_value.c_str() << std::endl;
});

強参照が適切でない場合、代わりに implements::get_weak を呼び出し、this の弱参照を取得できます。If a strong reference isn't appropriate, then you can instead call implements::get_weak to retrieve a weak reference to this. 弱参照では、現在のオブジェクトは存続しませんA weak reference does not keep the current object alive. そのため、メンバーにアクセスする前に、引き続き弱参照から強参照を取得できることを確認してください。So, just confirm that you can still retrieve a strong reference from the weak reference before accessing members.

event_source.Event([weak_this{ get_weak() }](auto&& ...)
{
    if (auto strong_this{ weak_this.get() })
    {
        std::wcout << strong_this->m_value.c_str() << std::endl;
    }
});

生ポインターをキャプチャする場合は、参照先のオブジェクトが存続することを確認する必要があります。If you capture a raw pointer, then you'll need to make sure you keep the pointed-to object alive.

デリゲートとしてメンバー関数を使用する場合If you use a member function as a delegate

ラムダ関数と共に、以上の原則はデリゲートとしてメンバー関数を使用する場合にも適用されます。As well as lambda functions, these principles also apply to using a member function as your delegate. 構文は異なります。いくつかのコードを見てみましょう。The syntax is different, so let's look at some code. 最初に紹介するのは、潜在的に安全ではないメンバー関数イベント ハンドラーです。生の this ポインターが使用されています。First, here's the potentially unsafe member function event handler, using a raw this pointer.

struct EventRecipient : winrt::implements<EventRecipient, IInspectable>
{
    winrt::hstring m_value{ L"Hello, World!" };

    void Register(EventSource& event_source)
    {
        event_source.Event({ this, &EventRecipient::OnEvent });
    }

    void OnEvent(IInspectable const& /* sender */, int /* args */)
    {
        std::wcout << m_value.c_str() << std::endl;
    }
};

これは、あるオブジェクトとそのメンバー関数を参照する従来の標準的な方法です。This is the standard, conventional way to refer to an object and its member function. これを安全にする目的で、Windows SDK のバージョン 10.0.17763.0 (Windows 10、バージョン 1809) 以降、ハンドラーが登録される箇所で強参照または弱参照を確立できます。To make this safe, you can—as of version 10.0.17763.0 (Windows 10, version 1809) of the Windows SDK—establish a strong or a weak reference at the point where the handler is registered. その箇所で、イベントを受信するオブジェクトは有効です。At that point, the event recipient object is known to be still alive.

強参照の場合、生の this ポインターの代わりに get_strong を呼び出します。For a strong reference, just call get_strong in place of the raw this pointer. C++/WinRT によって、結果的に生成されるデリゲートでは、現在のオブジェクトの強参照が保持されます。C++/WinRT ensures that the resulting delegate holds a strong reference to the current object.

event_source.Event({ get_strong(), &EventRecipient::OnEvent });

強参照をキャプチャすることは、ハンドラーが登録解除されてすべての未処理のコールバックが返された後にのみ、オブジェクトが破棄の対象になることを意味します。Capturing a strong reference means that your object will become eligible for destruction only after the handler has been unregistered and all outstanding callbacks have returned. ただし、その保証はイベントが発生した時点でのみ有効です。However, that guarantee is valid only at the time the event is raised. イベント ハンドラーが非同期の場合は、最初の中断ポイントの前に、コルーチンにクラス インスタンスへの強参照を与える必要があります (詳細とコードについては、このトピックで前述した「class-member コルーチンで this ポインターに安全にアクセスする」を参照してください)。If your event handler is asynchronous, then you'll have to give your coroutine a strong reference to the class instance before the first suspension point (for details, and code, see the Safely accessing the this pointer in a class-member coroutine section earlier in this topic). ただし、これにより、イベント ソースとオブジェクトの間に循環参照が作成されるため、イベントを取り消すことによって明示的に中断する必要があります。But that creates a circular reference between the event source and your object, so you need to explicitly break that by revoking your event.

弱参照の場合、get_weak を呼び出します。For a weak reference, call get_weak. C++/WinRT によって。結果的に生成されるデリゲートでは、弱参照が保持されます。C++/WinRT ensures that the resulting delegate holds a weak reference. 最後の瞬間、舞台裏では、弱参照を強参照に解決するよう、デリゲートによって試行されます。解決できた場合、そのメンバー関数のみが呼び出されます。At the last minute, and behind the scenes, the delegate attempts to resolve the weak reference to a strong one, and only calls the member function if it's successful.

event_source.Event({ get_weak(), &EventRecipient::OnEvent });

デリゲートがメンバー関数を呼び出す場合、C++/WinRT ではそのハンドラーが返されるまでオブジェクトが存続します。If the delegate does call your member function, then C++/WinRT will keep your object alive until your handler returns. ただし、ハンドラーが非同期の場合は中断ポイントで返されるため、最初の中断ポイントの前に、コルーチンにクラス インスタンスへの強参照を与える必要があります。However, if your handler is asynchronous, then it returns at suspension points, and so you'll have to give your coroutine a strong reference to the class instance before the first suspension point. この場合も、詳細については、このセクションで前述した「class-member コルーチンで this ポインターに安全にアクセスする」を参照してください。Again, for more info, see Safely accessing the this pointer in a class-member coroutine section earlier in this topic.

SwapChainPanel::CompositionScaleChanged を使用する弱参照の例A weak reference example using SwapChainPanel::CompositionScaleChanged

このコード例では、弱参照を説明するもう 1 つの方法として、SwapChainPanel::CompositionScaleChanged イベントを使用します。In this code example, we use the SwapChainPanel::CompositionScaleChanged event by way of another illustration of weak references. このコードでは、受信側の弱参照をキャプチャするラムダを使用し、イベント ハンドラーが登録されます。The code registers an event handler using a lambda that captures a weak reference to the recipient.

winrt::Windows::UI::Xaml::Controls::SwapChainPanel m_swapChainPanel;
winrt::event_token m_compositionScaleChangedEventToken;

void RegisterEventHandler()
{
    m_compositionScaleChangedEventToken = m_swapChainPanel.CompositionScaleChanged([weak_this{ get_weak() }]
        (Windows::UI::Xaml::Controls::SwapChainPanel const& sender,
        Windows::Foundation::IInspectable const& object)
    {
        if (auto strong_this{ weak_this.get() })
        {
            strong_this->OnCompositionScaleChanged(sender, object);
        }
    });
}

void OnCompositionScaleChanged(Windows::UI::Xaml::Controls::SwapChainPanel const& sender,
    Windows::Foundation::IInspectable const& object)
{
    // Here, we know that the "this" object is valid.
}

ラムダのキャプチャ句で、一時変数を作成し、このオブジェクトの弱参照を表示します。In the lamba capture clause, a temporary variable is created, representing a weak reference to this. ラムダ式の本文で、このオブジェクトの強参照を取得する場合は、OnCompositionScaleChanged 関数が呼び出されます。In the body of the lambda, if a strong reference to this can be obtained, then the OnCompositionScaleChanged function is called. これにより、OnCompositionScaleChanged 内でこのオブジェクトを安全に使用することができます。That way, inside OnCompositionScaleChanged, this can safely be used.

C++/WinRT の弱参照Weak references in C++/WinRT

以上、弱参照の使用を確認しました。Above, we saw weak references being used. 一般的に、弱参照は循環参照から抜けるときに最適です。In general, they're good for breaking cyclic references. たとえば、XAML ベースの UI フレームワークのネイティブ実装の場合—フレームワーク設計の歴史的背景が理由で—C++/WinRT の弱参照メカニズムは循環参照を処理するために必要になります。For example, for the native implementation of the XAML-based UI framework—because of the historical design of the framework—the weak reference mechanism in C++/WinRT is necessary to handle cyclic references. ただし、XAML 以外では、弱参照はおそらく使用する必要はありません (本質的に XAML 固有のものがあるというわけではありません)。Outside of XAML, though, you likely won't need to use weak references (not that there's anything inherently XAML-specific about them). むしろ、通常は、循環参照や弱参照が必要とならないように独自の C++/WinRT API を設計することができるはずです。Rather you should, more often than not, be able to design your own C++/WinRT APIs in such a way as to avoid the need for cyclic references and weak references.

宣言するすべての型について、いつどこで弱参照が必要になるかが C++/WinRT に対してすぐに明白になるわけではありません。For any given type that you declare, it's not immediately obvious to C++/WinRT whether or when weak references are needed. したがって、C++/WinRT では構造体テンプレート winrt::implements で弱参照サポートを自動的に提供し、そこから直接的または間接的に独自の C++/WinRT の型を派生します。So, C++/WinRT provides weak reference support automatically on the struct template winrt::implements, from which your own C++/WinRT types directly or indirectly derive. 利用に応じた料金制度であるため、オブジェクトが IWeakReferenceSource で実際に照会されない限り料金はかかりません。It's pay-for-play, in that it doesn't cost you anything unless your object is actually queried for IWeakReferenceSource. また、そのサポートを除外することを明示的に選択することができます。And you can choose explicitly to opt out of that support.

コード例Code examples

winrt::weak_ref 構造体テンプレートは、クラス インスタンスへの弱参照を取得するための 1 つのオプションです。The winrt::weak_ref struct template is one option for getting a weak reference to a class instance.

Class c;
winrt::weak_ref<Class> weak{ c };

または、winrt::make_weak ヘルパー関数を使用できます。Or, you can use the use the winrt::make_weak helper function.

Class c;
auto weak = winrt::make_weak(c);

弱参照を作成してもオブジェクト自体の参照カウントには影響しません。制御ブロックが割り当てられるだけです。Creating a weak reference doesn't affect the reference count on the object itself; it just causes a control block to be allocated. その制御ブロックが弱参照セマンティクスの実装を処理します。That control block takes care of implementing the weak reference semantics. その後、弱参照から強参照への昇格を試みて、成功した場合は使用することができます。You can then try to promote the weak reference to a strong reference and, if successful, use it.

if (Class strong = weak.get())
{
    // use strong, for example strong.DoWork();
}

他の強参照が存在する場合、weak_ref::get の呼び出しにより参照カウントが増分され、呼び出し元に強参照が返されます。Provided that some other strong reference still exists, the weak_ref::get call increments the reference count and returns the strong reference to the caller.

弱参照サポートの除外Opting out of weak reference support

弱参照サポートは自動です。Weak reference support is automatic. ただし、winrt::no_weak_ref マーカー構造体をテンプレート引数として基底クラスに渡すことによって、そのサポートを明示的に除外することを選択できます。But you can choose explicitly to opt out of that support by passing the winrt::no_weak_ref marker struct as a template argument to your base class.

winrt::implements から直接派生する場合。If you derive directly from winrt::implements.

struct MyImplementation: implements<MyImplementation, IStringable, no_weak_ref>
{
    ...
}

ランタイム クラスを作成している場合。If you're authoring a runtime class.

struct MyRuntimeClass: MyRuntimeClassT<MyRuntimeClass, no_weak_ref>
{
    ...
}

可変個引数パラメーター パックのどこにマーカー構造体が現れるかは関係ありません。It doesn't matter where in the variadic parameter pack the marker struct appears. 除外された型に対して弱参照を要求すると、コンパイラは "これは弱参照サポート専用です" というメッセージで知らせます。If you request a weak reference for an opted-out type, then the compiler will help you out with "This is only for weak ref support".

重要な APIImportant APIs