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>);
// 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&&, C>);
static_assert(false == std::reference_constructs_from_temporary_v<B&&, C&>);
static_assert(false == std::reference_constructs_from_temporary_v<B&&, C&&>);
static_assert(false == std::reference_constructs_from_temporary_v<B&&, int>);
}
出力
バージョン
言語
- C++23
処理系
- Clang: ???
- GCC: ???
- ICC: ???
- Visual C++: ???