最終更新日時(UTC): 2026年05月19日 02時39分44秒
Raclamusi が更新

履歴 編集

class template
<type_traits>

# std::reference_constructs_from_temporary(C++23)

    namespace std {
      template<class T, class U>
      struct reference_constructs_from_temporary;

      template<class T, class U>
        inline constexpr bool reference_constructs_from_temporary_v
           = reference_constructs_from_temporary<T, U>::value;
    }

## 概要

参照`T`が一時オブジェクト`U`を直接初期化（丸カッコによる初期化）で束縛した時、その一時オブジェクトの寿命を延長するかを判定する。

`tuple`や`pair`のコンストラクタなど、`explicit`なコンストラクタ・変換関数におけるダングリング参照の生成を回避するために使用される。

なお、コピー初期化（代入形式による初期化）における文脈では`reference_converts_from_temporary`が使用される。

## 要件

`T`と`U`のどちらも、完全型、`const`/`volatile`修飾された（あるいはされていない）`void`、要素数不明の配列型であること。

## 効果

`VAL<U>`を次のように定義する。

- `U`が参照型や関数型の場合、`declval<U>()`と同じ型と値カテゴリを持つ式
- `U`が参照型や関数型でない場合、型`U`である`prvalue`（ただし、`U`に`const`/`volatile`修飾があれば調整される）

`conjunction_v<is_reference<T>, is_constructible<T, U>>`が`true`かつ、`T t(VAL<U>);`において`t`が一時オブジェクトの寿命を延長する場合に`true_type`から派生し、そうでなければ`false_type`から派生する。

## 備考

多くのメタ関数は`prvalue`と`xvalue`を区別しないが、このメタ関数は区別する。例えば、右辺値参照に「戻り値の型が右辺値参照である関数」の戻り値を束縛することを考える。（再現コードは、説明の最後に付す）

この事自体は即座に不適格とはならない。しかし、その関数が実際には`prvalue`を返しているとすれば、その`prvalue`は戻り値の時点で右辺値参照に束縛されるため、それを右辺値参照に束縛したとしても寿命は延長されることはない。そのため、最終的にダングリング参照を生じることとなる。

このような場面においては、本メタ関数が`prvalue`と`xvalue`を、`prvalue`を`T`（参照なしの型）として、`xvalue`を`T&&`（右辺値参照である型、転送参照(Forwarding Reference) を意味しているわけではない）として区別すれば検出が可能となる。

    #include <iostream>

    struct S {
      S() { std::cout << "S construct" << std::endl; }
      ~S() { std::cout << "S destruct" << std::endl; }
    };

    S&& f() { return S{}; }

    int main() {
      std::cout << "1" << std::endl;

      // f の戻り値は s に束縛されて寿命が延長されたか？
      // 実際にはこの宣言の1文が終了した際に f の戻り値は破棄されている
      S&& s(f());

      std::cout << "2" << std::endl;
    }

### 出力

    1
    S construct
    S destruct
    2

## 例

    #include <type_traits>

    struct A {
        A() = default;
        A(int) {}
    };

    struct B : A {
        explicit B(int) {}
    };

    struct C {
        operator struct A() { return A{}; }
        explicit operator struct B() { return B{0}; }
    };

    int main()
    {
        // OK: わかりやすく寿命が延長されるタイプ
        static_assert(std::reference_constructs_from_temporary_v<int&&, int>);
        static_assert(std::reference_constructs_from_temporary_v<const int&, int>);
        static_assert(std::reference_constructs_from_temporary_v<A&&, B>);
        static_assert(std::reference_constructs_from_temporary_v<const A&&, B>);

        // OK: 変換されて rvalue になってから束縛されて寿命が延長されるタイプ
        static_assert(std::reference_constructs_from_temporary_v<int&&, long>);
        static_assert(std::reference_constructs_from_temporary_v<int&&, long&>);
        static_assert(std::reference_constructs_from_temporary_v<int&&, long&&>);

        // OK: explicit ではないので変換されて rvalue になってから束縛されて寿命が延長されるタイプ
        static_assert(std::reference_constructs_from_temporary_v<A&&, C>);
        static_assert(std::reference_constructs_from_temporary_v<A&&, C&>);
        static_assert(std::reference_constructs_from_temporary_v<A&&, C&&>);
        static_assert(std::reference_constructs_from_temporary_v<A&&, int>);

        // OK: 変換されて rvalue になってから束縛されて寿命が延長されるタイプ
        //     C → B の変換は explicit だが、丸カッコ初期化なので変換できる
        static_assert(std::reference_constructs_from_temporary_v<B&&, C>);
        static_assert(std::reference_constructs_from_temporary_v<B&&, C&>);
        static_assert(std::reference_constructs_from_temporary_v<B&&, C&&>);

        // NG: const ではない左辺値参照は寿命を延長しないタイプ
        //     1つ目3つ目はそもそも参照そのものが構築出来ない
        static_assert(false == std::reference_constructs_from_temporary_v<int&, int>);
        static_assert(false == std::reference_constructs_from_temporary_v<int&, int&>);
        static_assert(false == std::reference_constructs_from_temporary_v<int&, int&&>);

        // NG: 構築できないパターンと右辺値参照は区別するパターン
        static_assert(false == std::reference_constructs_from_temporary_v<int&&, int&>);
        static_assert(false == std::reference_constructs_from_temporary_v<int&&, int&&>);

        // NG: explicit なので変換出来ずに詰むパターン
        static_assert(false == std::reference_constructs_from_temporary_v<B&&, int>);
    }

### 出力

## バージョン

### 言語

- C++23

### 処理系

- Clang: ???
- GCC: ???
- ICC: ???
- Visual C++: ???

## 備考

- Bug 63604 - [C++11] A direct-initialization of a reference should use explicit conversion functions
  - GCCでは参照の直接初期化に `explicit` な変換関数が使われないバグがあり、上の例がコンパイルエラーになる。

## 関連項目

- `reference_converts_from_temporary`

## 参照

- P2255R2 A type trait to detect reference binding to temporary