#include #include #include #include #include #include namespace { template struct unary_t {}; template struct variadic_t {}; template struct derived_t : unary_t {}; } // namespace TEST_CASE("detect specializations", "[type_traits]") { STATIC_REQUIRE(stdx::is_specialization_of_v, unary_t>); STATIC_REQUIRE(stdx::is_type_specialization_of_v, unary_t>); STATIC_REQUIRE(stdx::is_specialization_of, unary_t>()); STATIC_REQUIRE(not stdx::is_specialization_of_v); STATIC_REQUIRE(not stdx::is_type_specialization_of_v); STATIC_REQUIRE(not stdx::is_specialization_of()); STATIC_REQUIRE(stdx::is_specialization_of_v, variadic_t>); STATIC_REQUIRE(stdx::is_type_specialization_of_v, variadic_t>); STATIC_REQUIRE(stdx::is_specialization_of, variadic_t>()); STATIC_REQUIRE(not stdx::is_specialization_of_v); STATIC_REQUIRE(not stdx::is_type_specialization_of_v); STATIC_REQUIRE(not stdx::is_specialization_of()); } TEST_CASE("derived types are not specializations", "[type_traits]") { STATIC_REQUIRE(not stdx::is_specialization_of_v, unary_t>); STATIC_REQUIRE( not stdx::is_type_specialization_of_v, unary_t>); STATIC_REQUIRE(not stdx::is_specialization_of, unary_t>()); } namespace { template struct value_unary_t {}; template struct value_variadic_t {}; template struct value_derived_t : value_unary_t {}; } // namespace TEST_CASE("detect specializations (value templates)", "[type_traits]") { STATIC_REQUIRE( stdx::is_value_specialization_of_v, value_unary_t>); STATIC_REQUIRE( stdx::is_specialization_of, value_unary_t>()); STATIC_REQUIRE(not stdx::is_value_specialization_of_v); STATIC_REQUIRE(not stdx::is_specialization_of()); STATIC_REQUIRE(stdx::is_value_specialization_of_v, value_variadic_t>); STATIC_REQUIRE( stdx::is_specialization_of, value_variadic_t>()); STATIC_REQUIRE( not stdx::is_value_specialization_of_v); STATIC_REQUIRE(not stdx::is_specialization_of()); } TEST_CASE("derived types are not specializations (value templates)", "[type_traits]") { STATIC_REQUIRE(not stdx::is_value_specialization_of_v, value_unary_t>); STATIC_REQUIRE( not stdx::is_specialization_of, value_unary_t>()); } namespace { enum E1 {}; enum struct E2 {}; } // namespace TEST_CASE("is_scoped_enum", "[type_traits]") { STATIC_REQUIRE(not stdx::is_scoped_enum_v); STATIC_REQUIRE(not stdx::is_scoped_enum_v); STATIC_REQUIRE(stdx::is_scoped_enum_v); STATIC_REQUIRE(not stdx::is_scoped_enum::value); STATIC_REQUIRE(not stdx::is_scoped_enum::value); STATIC_REQUIRE(stdx::is_scoped_enum::value); } TEST_CASE("type_identity", "[type_traits]") { STATIC_REQUIRE(std::is_same_v, void>); } TEST_CASE("type_or_t", "[type_traits]") { STATIC_REQUIRE( std::is_same_v, void>); STATIC_REQUIRE( std::is_same_v, float>); STATIC_REQUIRE(std::is_same_v, void>); } namespace { int value{}; struct add_value { add_value() = default; add_value(int init) { value = init; } template constexpr auto operator()() const -> void { value += T::value; } template constexpr auto operator()() const -> void { value += T; } }; struct add_values { template constexpr auto operator()() const { return (0 + ... + Ts::value); } template constexpr auto operator()() const { return (0 + ... + Ts); } }; } // namespace TEST_CASE("template_for_each with type list", "[type_traits]") { value = 0; using L = stdx::type_list, std::integral_constant>; stdx::template_for_each(add_value{}); CHECK(value == 3); } TEST_CASE("template_for_each with empty type list", "[type_traits]") { using L = stdx::type_list<>; stdx::template_for_each(add_value{17}); CHECK(value == 17); } TEST_CASE("template_for_each with value list", "[type_traits]") { using L = stdx::value_list<1, 2>; stdx::template_for_each(add_value{0}); CHECK(value == 3); } TEST_CASE("template_for_each with empty value list", "[type_traits]") { using L = stdx::value_list<>; stdx::template_for_each(add_value{17}); CHECK(value == 17); } TEST_CASE("template_for_each with index sequence", "[type_traits]") { using L = std::make_index_sequence<3>; stdx::template_for_each(add_value{0}); CHECK(value == 3); } TEST_CASE("apply_sequence with type list", "[type_traits]") { using L = stdx::type_list, std::integral_constant>; CHECK(stdx::apply_sequence(add_values{}) == 3); } TEST_CASE("apply_sequence with value list", "[type_traits]") { using L = stdx::value_list<1, 2>; CHECK(stdx::apply_sequence(add_values{}) == 3); } TEST_CASE("apply_sequence with index sequence", "[type_traits]") { using L = std::make_index_sequence<3>; CHECK(stdx::apply_sequence(add_values{}) == 3); STATIC_CHECK(std::is_same_v(add_values{})), std::size_t>); } TEST_CASE("is_same_unqualified", "[type_traits]") { STATIC_REQUIRE(stdx::is_same_unqualified_v); STATIC_REQUIRE(not stdx::is_same_unqualified_v); STATIC_REQUIRE(stdx::is_same_unqualified_v); STATIC_REQUIRE(stdx::is_same_unqualified_v); STATIC_REQUIRE(stdx::is_same_unqualified_v); STATIC_REQUIRE(stdx::is_same_unqualified_v); STATIC_REQUIRE(stdx::is_same_unqualified_v); STATIC_REQUIRE(stdx::is_same_unqualified_v); STATIC_REQUIRE(stdx::is_same_unqualified_v); STATIC_REQUIRE(stdx::is_same_unqualified_v); } // for a taxonomy of structural types below, see // https://en.cppreference.com/w/cpp/language/template_parameters#Non-type_template_parameter namespace structural { struct base_t { int x; auto f() { return 42; } }; using pmd_t = decltype(&base_t::x); using pmf_t = decltype(&base_t::f); struct derived_t : base_t { constexpr derived_t(int _x, int _y) : base_t{_x}, y{_y} {} int y; }; struct class_t : base_t { constexpr class_t(int _x, int _y) : base_t{_x}, d{_x, _y}, y{_y} {} derived_t d; int y; }; enum struct enum_t {}; constexpr auto l = [](int) { return 42; }; using lambda_t = decltype(l); using union_t = union { int x; int y; }; } // namespace structural TEST_CASE("structural types", "[type_traits]") { STATIC_REQUIRE(stdx::is_structural_v); STATIC_REQUIRE(stdx::is_structural_v); STATIC_REQUIRE(stdx::is_structural_v); STATIC_REQUIRE(stdx::is_structural_v); STATIC_REQUIRE(stdx::is_structural_v); STATIC_REQUIRE(stdx::is_structural_v); STATIC_REQUIRE(stdx::is_structural_v); #if __cpp_nontype_template_args >= 201911L STATIC_REQUIRE(stdx::is_structural_v); STATIC_REQUIRE(stdx::is_structural_v); STATIC_REQUIRE(stdx::is_structural_v); STATIC_REQUIRE(stdx::is_structural_v); STATIC_REQUIRE(stdx::is_structural_v); STATIC_REQUIRE(stdx::is_structural_v); #endif } namespace non_structural { struct S { ~S() {} // nontrivial destructor }; } // namespace non_structural TEST_CASE("non-structural types", "[type_traits]") { STATIC_REQUIRE(not stdx::is_structural_v); } namespace { template struct long_type_name {}; using A = long_type_name; using B = long_type_name; using C = long_type_name; } // namespace TEST_CASE("type shrinkage (by type)", "[type_traits]") { using X = stdx::shrink_t; #if __cplusplus >= 202002L STATIC_CHECK(stdx::type_as_string().size() < stdx::type_as_string().size()); #endif STATIC_CHECK(std::is_same_v, C>); } TEST_CASE("type shrinkage (by value)", "[type_traits]") { auto c = C{}; auto x = stdx::shrink(c); auto y = stdx::expand(x); #if __cplusplus >= 202002L STATIC_CHECK(stdx::type_as_string().size() < stdx::type_as_string().size()); #endif STATIC_CHECK(std::is_same_v); } TEST_CASE("nth type in pack", "[type_traits]") { STATIC_REQUIRE( std::is_same_v, float>); } #if __cplusplus >= 202002L TEST_CASE("nth value in pack", "[type_traits]") { STATIC_REQUIRE(stdx::nth_v<2, 0, true, 'b', 3> == 'b'); } #endif TEST_CASE("is_complete_v", "[type_traits]") { struct incomplete; STATIC_REQUIRE(stdx::is_complete_v); STATIC_REQUIRE(not stdx::is_complete_v); } TEST_CASE("is_same_template_v", "[type_traits]") { STATIC_REQUIRE(stdx::is_same_template_v, unary_t>); STATIC_REQUIRE( not stdx::is_same_template_v, variadic_t>); }