namespace std {
template <class T>
struct is_trivially_copyable;
template <class T>
inline constexpr bool is_trivially_copyable_v
= is_trivially_copyable<T>::value; // C++17
}
概要
型T
がトリビアルコピー可能か調べる。
要件
型remove_all_extents<T>::type
は、完全型か、const
/volatile
修飾された(あるいはされていない)void
でなければならない。
効果
is_trivially_copyable
は、T
がトリビアルコピー可能な型であるならばtrue_type
から派生し、そうでなければfalse_type
から派生する。
「トリビアルコピー可能な型」とは、「std::memcpy()
可能な型である」と言い換えることもできる。
トリビアルコピー可能な型の定義はこれまで2回変更されている。CWG issue 1734による変更と、C++20での変更の2つだ。
CWG issue 1734は2013年8月9日に報告されている。つまりC++14に対応した処理系はこれに対応している可能性があり、すくなくともC++17時点で規格書に修正が取り込まれている。
前提となる用語
user-provided
な関数とは、= default
/= delete
指定されていない、ユーザーによって宣言された関数である。
C++20
資格のある特殊メンバ関数
資格のある特殊メンバ関数とは、特殊メンバ関数のうち、次の条件を満たすものを言う。
= delete
指定されていない- 制約されている場合、それを満たしている
- 同じ種類の特殊メンバ関数の中でもっとも強く制約されている
同じ種類の特殊メンバ関数とは簡単に言うと、制約を除いて同一の宣言となる特殊メンバ関数のことである。
制約の詳細についてはコンセプトにある解説を参照のこと。
例えば下の例を見てほしい。optional
クラスは2つのコピーコンストラクタを持っている(#1と#2)。
これらは最初の引数の型が等しいので、2つの同じ種類のコピーコンストラクタを持っているといえる。
これらはいずれも= delete
指定されていない。
T=std::unique_ptr<int>
の場合、#1と#2はどちらも制約を満たさないので資格のあるコピーコンストラクタを持たない。
T=std::string
の場合、#2のみ制約を満たすので#2だけが資格のあるコピーコンストラクタである。
T=int
の場合、#1と#2はどちらも制約を満たすが、#1のほうが#2より強く制約されているので、#1だけが資格のあるコピーコンストラクタである。
#include <type_traits>
template <typename T>
concept trivially_copy_constructible = std::is_trivially_copy_constructible_v<T>;
template <typename T>
struct optional {
// #1
optional(optional const&)
requires trivially_copy_constructible<T> && std::copy_constructible<T>
= default;
// #2
optional(optional const& rhs)
requires std::copy_constructible<T>;
};
トリビアルコピー可能な型
トリビアルコピー可能な型とは、次の条件を満たすものをいう。
- 少なくとも1つの資格のあるコピー/ムーブ コンストラクタ/代入演算子がある
- 資格のあるコピー/ムーブ コンストラクタ/代入演算子それぞれはtrivialである
- trivialで
= delete
指定されていないデストラクタを持つ
これをもうすこし具体的に解釈する。
まずそのクラス自体に対して次の条件を満たす必要がある。
- デストラクタはvirtualではない
- デストラクタは
= delete
指定されていない - デストラクタと全ての資格のあるコピー/ムーブ コンストラクタ/代入演算子は
user-provided
ではない - virtual関数を持たず、virtual基本クラスも持たない
- 少なくとも1つの資格のあるコピー/ムーブ コンストラクタ/代入演算子がある
対象となるクラスの非静的メンバ変数及び全ての基底クラスもトリビアルコピー可能でなければならない
C++17もしくはCWG issue 1734が適用された処理系
トリビアルコピー可能な型とは、次の条件を満たすものをいう。
- 全てのコピー/ムーブ コンストラクタ/代入演算子はtrivialもしくは
= delete
指定されている - コピー/ムーブ コンストラクタ/代入演算子のうち少なくとも1つは
= delete
指定されていない - trivialで
= delete
指定されていないデストラクタを持つ
これをもうすこし具体的に解釈する。
まずそのクラス自体に対して次の条件を満たす必要がある。
- デストラクタはvirtualではない
- デストラクタは
= delete
指定されていない - デストラクタと全てのコピー/ムーブ コンストラクタ/代入演算子は
user-provided
ではない - virtual関数を持たず、virtual基本クラスも持たない
- コピー/ムーブ コンストラクタ/代入演算子のうち少なくとも1つは
= delete
指定されていない
対象となるクラスの非静的メンバ変数及び全ての基底クラスもトリビアルコピー可能でなければならない
CWG issue 1734が適用されていないC++14以前の処理系
トリビアルコピー可能な型とは、次の条件を満たすものをいう。
- 非トリビアルなコピーコンストラクタを持っていないこと
- 非トリビアルなムーブコンストラクタを持っていないこと
- 非トリビアルなコピー代入演算子を持っていないこと
- 非トリビアルなムーブ代入演算子を持っていないこと
- トリビアルなデストラクタを持っていること
これをもうすこし具体的に解釈する。
まずそのクラス自体に対して次の条件を満たす必要がある。
- デストラクタはvirtualではない
- デストラクタと全てのコピー/ムーブ コンストラクタ/代入演算子は
user-provided
ではない - virtual関数を持たず、virtual基本クラスも持たない
対象となるクラスの非静的メンバ変数及び全ての基底クラスもトリビアルコピー可能でなければならない
しかし、デストラクタが削除されていてもトリビアルコピー可能となる問題があった。
例
#include <type_traits>
#include <concepts>
#include <memory>
#include <string>
struct C1 {
// 非トリビアルな特殊関数を持っていない
// 特殊関数ではないメンバ関数は持っている
int f(int x) const { return x; }
};
struct C2 {
// デストラクタはuser-provided
~C2() {}
};
struct TrivialDestructor {
// デストラクタはdefault指定されており、user-providedではない
~TrivialDestructor() = default;
};
struct DeletedDestructor {
// デストラクタはdeleteされている
~DeletedDestructor() = delete;
};
#ifdef __cpp_concepts
template <typename T>
concept trivially_copy_constructible = std::is_trivially_copy_constructible_v<T>;
template <typename T>
struct optional {
alignas(T) std::byte value[sizeof(T)];
bool engaged;
// #1: default指定されており、user-providedではない
optional(optional const&)
requires trivially_copy_constructible<T> && std::copy_constructible<T>
= default;
// #2: user-providedなコピーコンストラクタ
optional(optional const& rhs)
requires std::copy_constructible<T>
: engaged(rhs.engaged)
{
if (engaged) {
new (value) T(rhs.value);
}
}
};
#endif
// 組み込み型は全てトリビアルコピー可能
static_assert(std::is_trivially_copyable<int>::value == true, "int is trivially copyable");
static_assert(std::is_trivially_copyable<int*>::value == true, "int* is trivially copyable");
// ユーザー定義型
static_assert(std::is_trivially_copyable<C1>::value == true, "C1 is trivially copyable");
static_assert(std::is_trivially_copyable<C2>::value == false, "C2 isn't trivially copyable");
static_assert(std::is_trivially_copyable<TrivialDestructor>::value == true, "TrivialDestructor is trivially copyable");
// CWG issue 1734が適用された環境ではトリビアルコピー可能ではない
static_assert(std::is_trivially_copyable<DeletedDestructor>::value == false, "DeletedDestructor isn't trivially copyable");
#ifdef __cpp_concepts
// #1も#2も資格のあるコピーコンストラクタではなく、他に資格のあるコピー/ムーブ コンストラクタ/代入演算子がないためトリビアルコピー可能ではない
static_assert(std::is_trivially_copyable<optional<std::unique_ptr<int>>>::value == false, "std::unique_ptr<int> isn't trivially copyable");
// #2が資格のあるコピーコンストラクタであるが、user-providedであるためトリビアルコピー可能ではない
static_assert(std::is_trivially_copyable<optional<std::string>>::value == false, "optional<std::string> isn't trivially copyable");
// #1が資格のあるコピーコンストラクタであるのでトリビアルコピー可能
static_assert(std::is_trivially_copyable<optional<int>>::value == true, "optional<int> is trivially copyable");
#endif
int main() {}
出力
バージョン
言語
- C++11
処理系
- Clang: 3.0 ✅
- GCC: 5.0 ✅
- Visual C++: 2012 ✅, 2013 ✅, 2015 ✅
- 2012~2013は、正しく実装されていない。
is_trivially_copy_constructible<T, U>
と同一の実装になっている。
- 2012~2013は、正しく実装されていない。
参照
- LWG Issue 2015. Incorrect pre-conditions for some type traits
- C++11では要件が「型
T
は完全型であるか、const
/volatile
修飾された(あるいはされていない)void
か、要素数不明の配列型でなければならない。」だったが、これは間違いであるため、C++14で「型remove_all_extents<T>::type
は、完全型か、const
/volatile
修飾された(あるいはされていない)void
でなければならない。」に変更された。
- C++11では要件が「型
- P0006R0 Adopt Type Traits Variable Templates from Library Fundamentals TS for C++17
- CWG issue 1734. Nontrivial deleted copy functions
- P0848R3 Conditionally Trivial Special Member Functions