前回の投稿「型特性ライブラリ:型チェック」では、型特性ライブラリを使用したコンパイル時の型チェックを紹介しました。今日は、コンパイル時の型比較について書きます。
型特性ライブラリを使用すると、コンパイル時に型を比較できます。コンパイル時とは、実行時にコストがかからないことを意味します。
型の比較
型特性ライブラリは、C++11 で 3 種類の比較をサポートします:
- is_base_of<ベース、派生>
- is_convertible
- 同じです
C++20 では、さらに
- is_pointer_interconvertible_with_class
- is_pointer_interconvertible_base_of<ベース、派生>
簡単にするために、C++11 メタ関数についてのみ記述します。
そのメンバー値のおかげで、各クラス テンプレートは true または false を返すため、static_assert に最適です。
// compare.cpp #include <cstdint> #include <iostream> #include <type_traits> class Base{}; class Derived: public Base{}; int main(){ std::cout << std::boolalpha << '\n'; std::cout << "std::is_base_of<Base,Derived>::value: " << std::is_base_of<Base,Derived>::value << '\n'; std::cout << "std::is_base_of<Derived,Base>::value: " << std::is_base_of<Derived,Base>::value << '\n'; std::cout << "std::is_base_of<Derived,Derived>::value: " << std::is_base_of<Derived,Derived>::value << '\n'; // static_assert(std::is_base_of<Derived,Base>::value,"Derived is not base of Base"); // (1) std::cout << '\n'; std::cout << "std::is_convertible<Base*,Derived*>::value: " << std::is_convertible<Base*,Derived*>::value << '\n'; std::cout << "std::is_convertible<Derived*,Base*>::value: " << std::is_convertible<Derived*,Base*>::value << '\n'; std::cout << "std::is_convertible<Derived*,Derived*>::value: " << std::is_convertible<Derived*,Derived*>::value << '\n'; // static_assert(std::is_convertible<Base*,Derived*>::value,"Base* can not be converted to Derived*"); // (2) std::cout << '\n'; std::cout << "std::is_same<int, int32_t>::value: " << std::is_same<int, int32_t>::value << '\n'; std::cout << "std::is_same<int, int64_t>::value: " << std::is_same<int, int64_t>::value << '\n'; std::cout << "std::is_same<long int, int64_t>::value: " << std::is_same<long int, int64_t>::value << '\n'; // static_assert(std::is_same<int, int64_t>::value,"int is not the same type as int64_t"); // (3) std::cout << '\n'; }
プログラムの出力に驚かないでください。
static_assert
を使用する場合 行 (1)、(2)、および (3) では、コンパイル時にアサーションが発生します。
Web サイト cppreference.com には、すべてのメタ関数 std::is_base_of,std::is_convertible,
の可能な実装が含まれています。 、および std::is_same です。それらを研究することは非常に興味深く、やりがいがあります。
可能な実装
まず、3 つのメタ関数の可能な実装を次に示します。 std::is_same に基づく最も単純なものから始めましょう。
std::is_same
次の例では、namespace rgr
を使用しています。 私の実装を C++ 標準実装と区別するため。
// isSame.cpp #include <iostream> #include <type_traits> namespace rgr { template<class T, T v> struct integral_constant { static constexpr T value = v; typedef T value_type; typedef integral_constant type; constexpr operator value_type() const noexcept { return value; } constexpr value_type operator()() const noexcept { return value; } //since c++14 }; typedef integral_constant<bool, true> true_type; // (2) typedef integral_constant<bool, false> false_type; template<class T, class U> struct is_same : false_type {}; // (3) template<class T> struct is_same<T, T> : true_type {}; } int main() { std::cout << '\n'; std::cout << std::boolalpha; std::cout << "rgr::is_same<int, const int>::value: " << rgr::is_same<int, const int>::value << '\n'; // (1) std::cout << "rgr::is_same<int, volatile int>::value: " << rgr::is_same<int, volatile int>::value << '\n'; std::cout << "rgr::is_same<int, int>::value: " << rgr::is_same<int, int>::value << '\n'; std::cout << '\n'; std::cout << "std::is_same<int, const int>::value: " << std::is_same<int, const int>::value << '\n'; std::cout << "std::is_same<int, volatile int>::value: " << std::is_same<int, volatile int>::value << '\n'; std::cout << "std::is_same<int, int>::value: " << std::is_same<int, int>::value << '\n'; std::cout << '\n'; }
簡単なメモ:関数テンプレートの呼び出し rgr::is_same<int, const int>
(1 行目) 式 rgr::false_type::value
の呼び出しを引き起こします (2 行目) std::is_same<>
のため false_type
から派生 (3 行目)。 rgr::false_type::value
rgr::integral_constant<bool, false>::value
のエイリアスです (2行目)。この例では、静的な constexpr
を使用しています クラス integral_constant
の値 . integral_constant
型特性関数の基本クラスです。
次の出力を調べると、興味深い事実が 2 つあります。私の実装 rgr::is_same
std::is_same
のような振る舞い 、および const
と volatile
タイプの一部です。
メタ関数 is_same に基づいてメタ関数 isSameIgnoringConstVolatile を実装するのは非常に簡単です。
// isSameIgnoringConstVolatile.cpp #include <iostream> #include <type_traits> namespace rgr { template<class T, T v> struct integral_constant { static constexpr T value = v; typedef T value_type; typedef integral_constant type; constexpr operator value_type() const noexcept { return value; } constexpr value_type operator()() const noexcept { return value; } //since c++14 }; typedef integral_constant<bool, true> true_type; typedef integral_constant<bool, false> false_type; template<class T, class U> struct is_same : false_type {}; template<class T> struct is_same<T, T> : true_type {}; template<typename T, typename U> // (1) struct isSameIgnoringConstVolatile: rgr::integral_constant< bool, rgr::is_same<typename std::remove_cv<T>::type, typename std::remove_cv<U>::type>::value > {}; } int main() { std::cout << '\n'; std::cout << std::boolalpha; std::cout << "rgr::isSameIgnoringConstVolatile<int, const int>::value: " << rgr::isSameIgnoringConstVolatile<int, const int>::value << '\n'; std::cout << "rgr::is_same<int, volatile int>::value: " << rgr::isSameIgnoringConstVolatile<int, volatile int>::value << '\n'; std::cout << "rgr::isSameIgnoringConstVolatile<int, int>::value: " << rgr::isSameIgnoringConstVolatile<int, int>::value << '\n'; std::cout << '\n'; }
メタ関数 isSameIgnoringConstVolatile
rgr::integral_constant
から派生 関数 std::remove_cv
を使用します const
を削除するには または volatile
その種類から。ご想像のとおり、std::remove_cv
型特性ライブラリの関数であり、コンパイル時に型を変更できます。型の変更については、次の投稿で詳しく書きます。
最後に、プログラムの出力は次のとおりです。
2 つのメタ関数 std::is_base_of と std::is_convertible
を詳しく見てみましょう。 .
以下は、cppreference.com からの可能な実装です。
std::is_base_of
namespace details { template <typename B> std::true_type test_pre_ptr_convertible(const volatile B*); template <typename> std::false_type test_pre_ptr_convertible(const volatile void*); template <typename, typename> auto test_pre_is_base_of(...) -> std::true_type; template <typename B, typename D> auto test_pre_is_base_of(int) -> decltype(test_pre_ptr_convertible<B>(static_cast<D*>(nullptr))); } template <typename Base, typename Derived> struct is_base_of : std::integral_constant< bool, std::is_class<Base>::value && std::is_class<Derived>::value && decltype(details::test_pre_is_base_of<Base, Derived>(0))::value > { };
std::is_convertible
namespace detail { template<class T> auto test_returnable(int) -> decltype( void(static_cast<T(*)()>(nullptr)), std::true_type{} ); template<class> auto test_returnable(...) -> std::false_type; template<class From, class To> auto test_implicitly_convertible(int) -> decltype( void(std::declval<void(&)(To)>()(std::declval<From>())), std::true_type{} ); template<class, class> auto test_implicitly_convertible(...) -> std::false_type; } // namespace detail template<class From, class To> struct is_convertible : std::integral_constant<bool, (decltype(detail::test_returnable<To>(0))::value && decltype(detail::test_implicitly_convertible<From, To>(0))::value) || (std::is_void<From>::value && std::is_void<To>::value) > {};
std::is_same
を説明した理由がわかりましたか? .これが私の課題です。
私の挑戦
型特性関数の以前の実装について説明してください std::is_base_of
そして std::is_convertible
.このメールアドレスは、スパムボットから保護されています.表示するには JavaScript を有効にする必要があります。木曜日(12月2日)まで。各関数の最良の回答には、LeanPub バンドルのモダン C++ コレクションのクーポンが与えられます。
次の投稿で各機能のベストアンサーを公開し、あなたの名を挙げます。あなたのフルネームを言うべきなら、それを書いてください。
次は?
型特性ライブラリのおかげで、コンパイル時に型を変更できます。それについては次の投稿で書きます。