namespace std {
template <class T, class U>
struct is_swappable_with;
template <class T, class U>
inline constexpr bool is_swappable_with_v = std::is_swappable_with<T, U>::value;
}
概要
型T
とU
の値が、swap関数によって入れ替え可能かどうかを調べる。
要件
型T
とU
が、完全型であること。もしくはconst
/volatile
修飾された(あるいはされていない)void
か、要素数不明の配列型であること。
効果
型T
とU
の間でstd::swappable_with
要件を満たしていればtrue_type
から派生し、そうでなければfalse_type
から派生する。
型T
とU
は参照でなければswapできないのでT
とU
が参照でない場合、結果はfalse
となる。
簡単に説明するとstd::swappable_with
要件を満たすとは、2引数を取りその引数の値を入れ替えるswap関数が以下のようなコードで見つかることである。
#include <utility>
template<typename T, typename U>
swappable_with(T& t, U& u) {
using std::swap;
//非メンバ関数のswap(t, u)が見つかる必要がある
swap(t, u);
//tとuは入れ替わっている
//引数を逆にしても同じく見つかる必要がある
swap(u, t);
//tとuは呼び出し時と同じに戻る
}
備考
このメタ関数はT
とU
についてのswap関数の直接のコンテキストの妥当性(そのシグネチャで有効なswapがあるかどうか)のみをチェックする。そのため、結果がtrue
となったとしてもswap関数の呼び出しができることは保証されない(その他の要因によりコンパイルエラーが発生する可能性がある)。
また同様に、結果がtrue
となっても見つかったswap関数がswap動作をするかどうかも保証しない。
例
#include <type_traits>
#include <iostream>
template<typename T>
struct non_swappable {};
template<typename T>
void swap(int&, non_swappable<T>&){}
template<typename T>
void swap(non_swappable<T>& lhs, int& rhs) {
int a = rhs;
//以下の代入は定義されていない
rhs = lhs;
lhs = a;
}
int main()
{
std::cout << std::boolalpha;
std::cout << std::is_swappable_with<int&, int&>::value << std::endl;
//参照でなければならない
std::cout << std::is_swappable_with<int, int>::value << std::endl;
std::cout << std::is_swappable_with<int&, double&>::value << std::endl;
//実際には呼び出し不可であり、swap動作もしないが、定義が見つかるためにtrueになる
std::cout << std::is_swappable_with<non_swappable<int>&, int&>::value << std::endl;
std::cout << std::is_swappable_with<int&, non_swappable<int>&>::value << std::endl;
//ここのコメントを外すとコンパイルエラー
/*non_swappable nonswap{};
int n{};
swap(nonswap, n);*/
}
出力
true
false
false
true
true
バージョン
言語
- C++17
処理系
- Clang: ??
- GCC: ??
- Visual C++: 2015 update3 ✅, 2017 ✅
- 2015 update3では、インテリセンスで表示されないが変数テンプレート共々定義されており利用可能である