#include #include #include #include using namespace c10::guts; namespace { namespace test_function_traits { static_assert( std::is_same< void, typename function_traits::return_type>::value, ""); static_assert( std::is_same::return_type>:: value, ""); static_assert( std::is_same< typelist::typelist, typename function_traits::parameter_types>::value, ""); static_assert( std::is_same< typelist::typelist, typename function_traits::parameter_types>::value, ""); static_assert( std::is_same< bool, typename make_function_traits_t>:: return_type>::value, ""); static_assert( std::is_same< void, typename make_function_traits_t>:: return_type>::value, ""); static_assert( std::is_same< typelist::typelist, typename make_function_traits_t>:: parameter_types>::value, ""); static_assert( std::is_same< typelist::typelist, typename make_function_traits_t>:: parameter_types>::value, ""); static_assert( std::is_same< bool(int, float), typename make_function_traits_t>:: func_type>::value, ""); static_assert( std::is_same< void(int, float), typename make_function_traits_t>:: func_type>::value, ""); } // namespace test_function_traits struct MovableOnly { constexpr MovableOnly(int val_) : val(val_) { /* no default constructor */ } MovableOnly(const MovableOnly&) = delete; MovableOnly(MovableOnly&&) = default; MovableOnly& operator=(const MovableOnly&) = delete; MovableOnly& operator=(MovableOnly&&) = default; friend bool operator==(const MovableOnly& lhs, const MovableOnly& rhs) { return lhs.val == rhs.val; } private: int val; }; template using is_my_movable_only_class = std::is_same>>; struct CopyCounting { int move_count; int copy_count; CopyCounting() : move_count(0), copy_count(0) {} CopyCounting(const CopyCounting& rhs) : move_count(rhs.move_count), copy_count(rhs.copy_count + 1) {} CopyCounting(CopyCounting&& rhs) : move_count(rhs.move_count + 1), copy_count(rhs.copy_count) {} CopyCounting& operator=(const CopyCounting& rhs) { move_count = rhs.move_count; copy_count = rhs.copy_count + 1; return *this; } CopyCounting& operator=(CopyCounting&& rhs) { move_count = rhs.move_count + 1; copy_count = rhs.copy_count; return *this; } }; template using is_my_copy_counting_class = std::is_same>>; namespace test_extract_arg_by_filtered_index { class MyClass {}; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, ExtractArgByFilteredIndex) { auto a1 = extract_arg_by_filtered_index( 3, "bla", MyClass(), 4, nullptr, 5); auto a2 = extract_arg_by_filtered_index( 3, "bla", MyClass(), 4, nullptr, 5); auto a3 = extract_arg_by_filtered_index( 3, "bla", MyClass(), 4, nullptr, 5); EXPECT_EQ(3, a1); EXPECT_EQ(4, a2); EXPECT_EQ(5, a3); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, ExtractArgByFilteredIndex_singleInput) { auto a1 = extract_arg_by_filtered_index(3); EXPECT_EQ(3, a1); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, ExtractArgByFilteredIndex_movableOnly) { MovableOnly a1 = extract_arg_by_filtered_index( 3, MovableOnly(3), "test", MovableOnly(1)); MovableOnly a2 = extract_arg_by_filtered_index( 3, MovableOnly(3), "test", MovableOnly(1)); EXPECT_EQ(MovableOnly(3), a1); EXPECT_EQ(MovableOnly(1), a2); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, ExtractArgByFilteredIndex_onlyCopiesIfNecessary) { CopyCounting source; CopyCounting source2; CopyCounting a1 = extract_arg_by_filtered_index( 3, CopyCounting(), "test", source, std::move(source2)); // NOLINTNEXTLINE(bugprone-use-after-move) CopyCounting a2 = extract_arg_by_filtered_index( 3, CopyCounting(), "test", source, std::move(source2)); // NOLINTNEXTLINE(bugprone-use-after-move) CopyCounting a3 = extract_arg_by_filtered_index( 3, CopyCounting(), "test", source, std::move(source2)); EXPECT_EQ(1, a1.move_count); EXPECT_EQ(0, a1.copy_count); EXPECT_EQ(0, a2.move_count); EXPECT_EQ(1, a3.move_count); EXPECT_EQ(0, a3.copy_count); EXPECT_EQ(1, a2.copy_count); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, ExtractArgByFilteredIndex_onlyMovesIfNecessary) { CopyCounting source; CopyCounting source2; CopyCounting&& a1 = extract_arg_by_filtered_index( 3, std::move(source), "test", std::move(source2)); // NOLINTNEXTLINE(bugprone-use-after-move) CopyCounting a2 = extract_arg_by_filtered_index( 3, std::move(source), "test", std::move(source2)); EXPECT_EQ(0, a1.move_count); EXPECT_EQ(0, a1.copy_count); EXPECT_EQ(1, a2.move_count); EXPECT_EQ(0, a2.copy_count); } template using is_true = std::true_type; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST( MetaprogrammingTest, ExtractArgByFilteredIndex_keepsLValueReferencesIntact) { MyClass obj; MyClass& a1 = extract_arg_by_filtered_index(3, obj, "test", obj); EXPECT_EQ(&obj, &a1); } } // namespace test_extract_arg_by_filtered_index namespace test_filter_map { class MyClass {}; struct map_to_double { template constexpr double operator()(T a) const { return static_cast(a); } }; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, FilterMap) { auto result = filter_map( map_to_double(), 3, "bla", MyClass(), 4, nullptr, 5); static_assert(std::is_same, decltype(result)>::value, ""); constexpr array expected{{3.0, 4.0, 5.0}}; EXPECT_EQ(expected, result); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, FilterMap_emptyInput) { auto result = filter_map(map_to_double()); static_assert(std::is_same, decltype(result)>::value, ""); constexpr array expected{{}}; EXPECT_EQ(expected, result); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, FilterMap_emptyOutput) { auto result = filter_map( map_to_double(), "bla", MyClass(), nullptr); static_assert(std::is_same, decltype(result)>::value, ""); constexpr array expected{{}}; EXPECT_EQ(expected, result); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, FilterMap_movableOnly_byRValue) { struct map_movable_by_rvalue { MovableOnly operator()(MovableOnly&& a) const { return std::move(a); } }; auto result = filter_map( map_movable_by_rvalue(), MovableOnly(5), "bla", nullptr, 3, MovableOnly(2)); static_assert( std::is_same, decltype(result)>::value, ""); constexpr array expected{{MovableOnly(5), MovableOnly(2)}}; EXPECT_EQ(expected, result); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, FilterMap_movableOnly_byValue) { struct map_movable_by_lvalue { MovableOnly operator()(MovableOnly a) const { return a; } }; auto result = filter_map( map_movable_by_lvalue(), MovableOnly(5), "bla", nullptr, 3, MovableOnly(2)); static_assert( std::is_same, decltype(result)>::value, ""); constexpr array expected{{MovableOnly(5), MovableOnly(2)}}; EXPECT_EQ(expected, result); } // See https://github.com/pytorch/pytorch/issues/35546 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST( MetaprogrammingTest, DISABLED_ON_WINDOWS(FilterMap_onlyCopiesIfNecessary)) { struct map_copy_counting_by_copy { CopyCounting operator()(CopyCounting v) const { return v; } }; CopyCounting source; CopyCounting source2; auto result = filter_map( map_copy_counting_by_copy(), CopyCounting(), "bla", nullptr, 3, source, std::move(source2)); static_assert( std::is_same, decltype(result)>::value, ""); EXPECT_EQ(0, result[0].copy_count); EXPECT_EQ(2, result[0].move_count); EXPECT_EQ(1, result[1].copy_count); EXPECT_EQ(1, result[1].move_count); EXPECT_EQ(0, result[2].copy_count); EXPECT_EQ(2, result[2].move_count); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST( MetaprogrammingTest, DISABLED_ON_WINDOWS(FilterMap_onlyMovesIfNecessary_1)) { struct map_copy_counting_by_move { CopyCounting operator()(CopyCounting&& v) const { return std::move(v); } }; CopyCounting source; auto result = filter_map( map_copy_counting_by_move(), CopyCounting(), "bla", nullptr, 3, std::move(source)); static_assert( std::is_same, decltype(result)>::value, ""); EXPECT_EQ(0, result[0].copy_count); EXPECT_EQ(1, result[0].move_count); EXPECT_EQ(0, result[1].copy_count); EXPECT_EQ(1, result[1].move_count); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, FilterMap_onlyMovesIfNecessary_2) { struct map_copy_counting_by_pointer { const CopyCounting* operator()(const CopyCounting& v) const { return &v; } }; CopyCounting source1; CopyCounting source2; auto result = filter_map( map_copy_counting_by_pointer(), "bla", nullptr, 3, source1, std::move(source2)); static_assert( std::is_same, decltype(result)>::value, ""); EXPECT_EQ(0, result[0]->copy_count); EXPECT_EQ(0, result[0]->move_count); EXPECT_EQ(0, result[1]->copy_count); EXPECT_EQ(0, result[1]->move_count); } } // namespace test_filter_map namespace test_tuple_elements { // note: not testing empty selection, as some compilers will raise // "parameter set but not used" in tuple_elements(). a good example // of the friction that comes with using these tools // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleElements_subsetSelection) { auto x = std::make_tuple(0, "HEY", 2.0); auto y = tuple_elements(x, std::index_sequence<0, 2>()); auto z = std::make_tuple(0, 2.0); EXPECT_EQ(y, z); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleElements_reorderSelection) { auto x = std::make_tuple(0, "HEY", 2.0); auto y = tuple_elements(x, std::index_sequence<0, 2, 1>()); auto z = std::make_tuple(0, 2.0, "HEY"); EXPECT_EQ(y, z); } } // namespace test_tuple_elements namespace test_tuple_take { // note: not testing empty prefix, see note on empty selection above. // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleTake_nonemptyPrefix) { auto x = std::make_tuple(0, "HEY", 2.0); auto y = tuple_take(x); auto z = std::make_tuple(0, "HEY"); EXPECT_EQ(y, z); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleTake_fullPrefix) { auto x = std::make_tuple(0, "HEY", 2.0); auto y = tuple_take(x); EXPECT_EQ(x, y); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleTake_negative) { auto x = std::make_tuple(0, "HEY", 2.0); auto y = tuple_take(x); auto z = std::make_tuple("HEY", 2.0); EXPECT_EQ(y, z); } } // namespace test_tuple_take namespace test_tuple_slice { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleSlice_middle) { auto x = std::make_tuple(0, "HEY", 2.0, false); auto y = tuple_slice(x); auto z = std::make_tuple("HEY", 2.0); EXPECT_EQ(y, z); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleSlice_full) { auto x = std::make_tuple(0, "HEY", 2.0); auto y = tuple_slice(x); EXPECT_EQ(x, y); } } // namespace test_tuple_slice namespace test_tuple_map { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleMap_simple) { auto result = tuple_map( std::tuple(3, 4, 5), [](int32_t a) -> int16_t { return a + 1; }); static_assert( std::is_same, decltype(result)>:: value, ""); EXPECT_EQ(4, std::get<0>(result)); EXPECT_EQ(5, std::get<1>(result)); EXPECT_EQ(6, std::get<2>(result)); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleMap_mapperTakesDifferentButConvertibleType) { auto result = tuple_map( std::tuple(3, 4, 5), [](int64_t a) -> int16_t { return a + 1; }); static_assert( std::is_same, decltype(result)>:: value, ""); EXPECT_EQ(4, std::get<0>(result)); EXPECT_EQ(5, std::get<1>(result)); EXPECT_EQ(6, std::get<2>(result)); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleMap_mapperTakesConstRef) { auto result = tuple_map( std::tuple(3, 4, 5), [](const int32_t& a) -> int16_t { return a + 1; }); static_assert( std::is_same, decltype(result)>:: value, ""); EXPECT_EQ(4, std::get<0>(result)); EXPECT_EQ(5, std::get<1>(result)); EXPECT_EQ(6, std::get<2>(result)); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleMap_mapsToDifferentTypes) { struct Mapper { std::string operator()(int32_t a) const { return std::to_string(a); } int32_t operator()(const std::string& a) const { return atoi(a.c_str()); } }; auto result = tuple_map(std::tuple(3, "4"), Mapper()); static_assert( std::is_same, decltype(result)>::value, ""); EXPECT_EQ("3", std::get<0>(result)); EXPECT_EQ(4, std::get<1>(result)); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleMap_differentiatesLRValueReferences) { struct Mapper { std::string operator()(std::string&& a) const { return "moved"; } std::string operator()(const std::string& a) const { return "copied"; } }; std::string str1, str2; auto result = tuple_map( std::tuple(str1, std::move(str2)), Mapper()); static_assert( std::is_same, decltype(result)>:: value, ""); EXPECT_EQ("copied", std::get<0>(result)); EXPECT_EQ("moved", std::get<1>(result)); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleMap_canWorkWithMovableOnlyType) { auto result = tuple_map( std::tuple(MovableOnly(7)), [](MovableOnly a) { return a; }); static_assert( std::is_same, decltype(result)>::value, ""); EXPECT_EQ(MovableOnly(7), std::get<0>(result)); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleMap_doesntUnecessarilyCopyValues) { auto result = tuple_map( std::tuple(CopyCounting()), [](CopyCounting a) { return a; }); static_assert( std::is_same, decltype(result)>::value, ""); EXPECT_EQ(4, std::get<0>(result).move_count); EXPECT_EQ(0, std::get<0>(result).copy_count); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleMap_doesntUnecessarilyMoveValues) { CopyCounting a; auto result = tuple_map( std::tuple(std::move(a)), [](CopyCounting&& a) -> CopyCounting&& { return std::move(a); }); static_assert( std::is_same, decltype(result)>::value, ""); EXPECT_EQ(&a, &std::get<0>(result)); EXPECT_EQ(0, std::get<0>(result).move_count); EXPECT_EQ(0, std::get<0>(result).copy_count); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleMap_canBeUsedWithAutoLambdas) { struct A final { int32_t func() { return 5; } }; struct B final { std::string func() { return "5"; } }; auto result = tuple_map(std::make_tuple(A(), B()), [](auto a) { return a.func(); }); static_assert( std::is_same, decltype(result)>::value, ""); EXPECT_EQ(5, std::get<0>(result)); EXPECT_EQ("5", std::get<1>(result)); } } // namespace test_tuple_map namespace test_tuple_concat { // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleConcat_zerotuples) { auto result = tuple_concat(); static_assert(std::is_same, decltype(result)>::value, ""); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleConcat_oneemptytuple) { auto result = tuple_concat(std::tuple<>()); static_assert(std::is_same, decltype(result)>::value, ""); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleConcat_onenonemptytuple) { auto result = tuple_concat(std::tuple(3)); static_assert(std::is_same, decltype(result)>::value, ""); EXPECT_EQ(3, std::get<0>(result)); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleConcat_twotuples) { auto result = tuple_concat( std::tuple(3, "4"), std::tuple(2.3, 15)); static_assert( std::is_same< std::tuple, decltype(result)>::value, ""); EXPECT_EQ(3, std::get<0>(result)); EXPECT_EQ("4", std::get<1>(result)); EXPECT_EQ(2.3, std::get<2>(result)); EXPECT_EQ(15, std::get<3>(result)); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleConcat_threetuples) { auto result = tuple_concat( std::tuple(3, "4"), std::tuple(2.3, 15), std::tuple("5", 3.2)); static_assert( std::is_same< std::tuple, decltype(result)>::value, ""); EXPECT_EQ(3, std::get<0>(result)); EXPECT_EQ("4", std::get<1>(result)); EXPECT_EQ(2.3, std::get<2>(result)); EXPECT_EQ(15, std::get<3>(result)); EXPECT_EQ("5", std::get<4>(result)); EXPECT_EQ(static_cast(3.2), std::get<5>(result)); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleConcat_emptytupleatbeginning) { auto result = tuple_concat( std::tuple<>(), std::tuple(2.3, 15), std::tuple("5", 3.2)); static_assert( std::is_same< std::tuple, decltype(result)>::value, ""); EXPECT_EQ(2.3, std::get<0>(result)); EXPECT_EQ(15, std::get<1>(result)); EXPECT_EQ("5", std::get<2>(result)); EXPECT_EQ(static_cast(3.2), std::get<3>(result)); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleConcat_emptytupleinmiddle) { auto result = tuple_concat( std::tuple(2.3, 15), std::tuple<>(), std::tuple("5", 3.2)); static_assert( std::is_same< std::tuple, decltype(result)>::value, ""); EXPECT_EQ(2.3, std::get<0>(result)); EXPECT_EQ(15, std::get<1>(result)); EXPECT_EQ("5", std::get<2>(result)); EXPECT_EQ(static_cast(3.2), std::get<3>(result)); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleConcat_emptytupleatend) { auto result = tuple_concat( std::tuple(2.3, 15), std::tuple("5", 3.2), std::tuple<>()); static_assert( std::is_same< std::tuple, decltype(result)>::value, ""); EXPECT_EQ(2.3, std::get<0>(result)); EXPECT_EQ(15, std::get<1>(result)); EXPECT_EQ("5", std::get<2>(result)); EXPECT_EQ(static_cast(3.2), std::get<3>(result)); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleConcat_workswithreferencesandpointers) { double val1 = 2.3; int16_t val2 = 15; std::string val3 = "hello"; float val4 = 3.2; auto result = tuple_concat( std::tuple(val1, val2), std::tuple(std::move(val3), &val4)); static_assert( std::is_same< std::tuple, decltype(result)>::value, ""); EXPECT_EQ(2.3, std::get<0>(result)); EXPECT_EQ(&val1, &std::get<0>(result)); EXPECT_EQ(15, std::get<1>(result)); EXPECT_EQ(&val2, &std::get<1>(result)); EXPECT_EQ("hello", std::get<2>(result)); EXPECT_EQ(&val3, &std::get<2>(result)); EXPECT_EQ(static_cast(3.2), *std::get<3>(result)); EXPECT_EQ(&val4, std::get<3>(result)); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleConcat_worksWithMovableOnlyTypes) { auto result = tuple_concat( std::tuple(1, 2), std::tuple(3)); static_assert( std::is_same< std::tuple, decltype(result)>::value, ""); EXPECT_EQ(MovableOnly(1), std::get<0>(result)); EXPECT_EQ(MovableOnly(2), std::get<1>(result)); EXPECT_EQ(MovableOnly(3), std::get<2>(result)); } // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) TEST(MetaprogrammingTest, TupleConcat_doesntCopyMoreThanNecessary) { auto result = tuple_concat( std::tuple(CopyCounting(), CopyCounting()), std::tuple(CopyCounting()), std::tuple(CopyCounting())); static_assert( std::is_same< std::tuple, decltype(result)>::value, ""); EXPECT_EQ(0, std::get<0>(result).copy_count); EXPECT_EQ(0, std::get<1>(result).copy_count); EXPECT_EQ(0, std::get<2>(result).copy_count); EXPECT_EQ(0, std::get<3>(result).copy_count); EXPECT_EQ(2, std::get<0>(result).move_count); EXPECT_EQ(2, std::get<1>(result).move_count); EXPECT_EQ(2, std::get<2>(result).move_count); EXPECT_EQ(2, std::get<3>(result).move_count); } } // namespace test_tuple_concat namespace test_concat_iseq { using std::index_sequence; using std::integer_sequence; static_assert(std::is_same, concat_iseq_t<>>::value, ""); static_assert( std::is_same, concat_iseq_t>>::value, ""); static_assert( std::is_same< index_sequence<>, concat_iseq_t, index_sequence<>>>::value, ""); static_assert( std::is_same, concat_iseq_t>>::value, ""); static_assert( std::is_same< index_sequence<4>, concat_iseq_t, index_sequence<>>>::value, ""); static_assert( std::is_same< index_sequence<4>, concat_iseq_t, index_sequence<4>>>::value, ""); static_assert( std::is_same< index_sequence<4>, concat_iseq_t, index_sequence<4>, index_sequence<>>>:: value, ""); static_assert( std::is_same< index_sequence<4, 2>, concat_iseq_t, index_sequence<2>>>::value, ""); static_assert( std::is_same< index_sequence<4, 2>, concat_iseq_t< index_sequence<>, index_sequence<4, 2>, index_sequence<>>>::value, ""); static_assert( std::is_same< index_sequence<4, 2, 9>, concat_iseq_t< index_sequence<>, index_sequence<4, 2>, index_sequence<9>>>::value, ""); static_assert( std::is_same< integer_sequence, concat_iseq_t< integer_sequence, integer_sequence>>::value, ""); } // namespace test_concat_iseq } // namespace