I was trying to reverse a c++14 std::index_sequence
and ran into problems with my original implementation that used inheritance. I found a workaround using local type aliases, but I would like to understand why the original code does not work.
Broken Reverse Using Inheritance
This was my first attempt at reversing a std::index_sequence
:
/// Helper class that appends an element onto an index_sequence.
/// Base case.
template<size_t, typename>
struct Append : std::index_sequence<> { };
template<size_t X, size_t... XS>
struct Append<X, std::index_sequence<XS...>> : std::index_sequence<XS..., X> { };
/// Reverse elements of a std::index_sequence
template<typename>
struct Reverse;
/// Base case
template<>
struct Reverse<std::index_sequence<>> : std::index_sequence<> { };
template<size_t X, size_t... XS>
struct Reverse<std::index_sequence<X, XS...>> : Append<X, Reverse<std::index_sequence<XS...>>> { };
This support function prints the contents of an std::index_sequence
:
template <size_t... XS>
void print_seq(std::index_sequence<XS...>)
{
std::cout << "size " << sizeof...(XS) << ": ";
bool Do[] = { (std::cout << XS << " ", true)... };
(void) Do;
std::cout << std::endl;
}
This implementation unfortunately did not work as I expected:
print_seq(Reverse<std::make_index_sequence<10>>{});
The output shows a size of 0 with no elements:
size 0:
Working Reverse Using Type Aliases
Then I slightly revised my original code to use type aliases instead of inherence. All the other logic should be exactly the same as the first example.
template<size_t, typename>
struct AppendUsingType {
using type = std::index_sequence<>;
};
template<size_t X, size_t... XS>
struct AppendUsingType<X, std::index_sequence<XS...>> {
using type = std::index_sequence<XS..., X> ;
};
template<typename>
struct ReverseUsingType;
template<>
struct ReverseUsingType<std::index_sequence<>> {
using type = std::index_sequence<>;
};
template<size_t X, size_t... XS>
struct ReverseUsingType<std::index_sequence<X, XS...>> {
using type = typename AppendUsingType<X, typename ReverseUsingType<std::index_sequence<XS...>>::type>::type;
};
Now when I inspect the type:
print_seq(typename ReverseUsingType<std::make_index_sequence<10>>::type{});
I see the correct output:
size 10: 9 8 7 6 5 4 3 2 1 0
Question
Even though I found a solution, I would really like to understand why the implementation using inheritance fails while the one using type aliases behaves as expected. I see this in both gcc and Clang, so I suspect there some reason in the language specification.
(Perhaps Related Question: typedef vs public inheritance in c++ meta-programming)