最終更新日時(UTC):
が更新

履歴 編集

concept
<concepts>

std::swappable(C++20)

namespace std {
  template<class T>
  concept swappable = requires(T& a, T& b) { ranges::swap(a, b); };

  template<class T, class U>
  concept swappable_with =
    common_reference_with<T, U> &&
    requires(T&& t, U&& u) {
      ranges::swap(std::forward<T>(t), std::forward<T>(t));
      ranges::swap(std::forward<U>(u), std::forward<U>(u));
      ranges::swap(std::forward<T>(t), std::forward<U>(u));
      ranges::swap(std::forward<U>(u), std::forward<T>(t));
    };
}

概要

swappable及びswappable_withは、指定された型TもしくはT, Uのオブジェクト間で、その値の交換操作(swap操作)が可能であることを表すコンセプトである。

swappable

#include <iostream>
#include <concepts>

template<typename T>
requires std::swappable<T>
void f(const char* name) {
  std::cout << name << " is swappable" << std::endl;
}

template<typename T>
void f(const char* name) {
  std::cout << name << " is not swappable" << std::endl;
}

namespace NS {
  struct swappable1 {
    int n = 0;

    swappable1(int m) : n(m) {}

    swappable1(swappable1&&) = delete;
  };

  // 非メンバ関数として定義
  void swap(swappable1& lhs, swappable1& rhs) {
    std::swap(lhs.n, rhs.n);
  }


  struct swappable2 {
    double d = 0.0;

    swappable2(double v) : d(v) {}

    swappable2(swappable2&&) = delete;

    // Hidden friendsな関数として定義
    friend void swap(swappable2& lhs, swappable2& rhs) {
      std::swap(lhs.d, rhs.d);
    }
  };
}

struct not_swappable {
  not_swappable(not_swappable&&) = delete;
  not_swappable& operator==(not_swappable&&) = delete;
};

int main() {
  f<int>("int");
  f<int&&>("int&&");
  f<NS::swappable1>("NS::swappable1");
  f<NS::swappable2>("NS::swappable2");
  f<int[5]>("int[5]");

  std::cout << "\n";

  f<const int>("const int");
  f<not_swappable>("not_swappable");
}

出力

int is swappable
int&& is swappable
NS::swappable1 is swappable
NS::swappable2 is swappable
int[5] is swappable

const int is not swappable
not_swappable is not swappable

swappable_with

#include <iostream>
#include <concepts>

template<typename T, typename U>
requires std::swappable_with<T, U>
void f(const char* name, const char* name2) {
  std::cout << name << " is swappable with " << name2 << std::endl;
}

template<typename T, typename U>
void f(const char* name, const char* name2) {
  std::cout << name << " is not swappable with " << name2 << std::endl;
}

namespace NS {

  struct S1 {
    std::size_t n = 0;

    operator std::size_t() const {
      return this->n;
    }

    //swappable_withであるためには、自身がswappableである必要がある
    friend void swap(S1& a, S1& b) {
      std::swap(a.n, b.n);
    }
  };

  struct S2 {
    std::size_t s = 0;

    operator std::size_t() const {
      return this->s;
    }

    //swappable_withであるためには、自身がswappableである必要がある
    friend void swap(S2& a, S2& b) {
      std::swap(a.s, b.s);
    }
  };

  void swap(S1& s1, S2& s2) {
    std::swap(s1.n, s2.s);
  }

  //引数順を逆にしたものも必要
  void swap(S2& s2, S1& s1) {
    swap(s1, s2);
  }
}

//std::common_referenceおよびstd::common_reference_withにアダプトする
namespace std {
  template<template<class> class TQual, template<class> class UQual>
  struct basic_common_reference<NS::S1, NS::S2, TQual, UQual> {
    using type = const std::size_t&;
  };

  //対称性のために引数順を逆にしたものも定義する
  template<template<class> class TQual, template<class> class UQual>
  struct basic_common_reference<NS::S2, NS::S1, TQual, UQual> {
    using type = const std::size_t&;
  };
}

int main() {
  f<NS::S1&, NS::S2&>("NS::S1&", "NS::S2&");
  f<NS::S2&, NS::S1&>("NS::S2&", "NS::S1&");

  //右辺値参照バージョンを用意してないので、参照修飾なしだとswappable_withとならない
  f<NS::S1, NS::S2>("NS::S1", "NS::S2");
  f<NS::S2, NS::S1>("NS::S2", "NS::S1");
}

出力

NS::S1& is swappable with NS::S2&
NS::S2& is swappable with NS::S1&
NS::S1 is not swappable with NS::S2
NS::S2 is not swappable with NS::S1

バージョン

言語

  • C++20

処理系

関連項目

参照