Reputation: 2261
I've been trying to write a zero_sequence
type which either creates an index_sequence
of a specified number of zeros or uses an existing
index_sequence
as to produce a new one with the same number of values, but all zero. I'm perplexed as to why this is happening:
template<typename> struct zero_sequence;
template<size_t...I>
struct zero_sequence<index_sequence<I...>> : index_sequence<(I*0u)...>{};
int main( int argc, char *argv[] ) {
using A = index_sequence<0,1,2,3,4>;
using B = make_index_sequence<5>;
using A0 = zero_sequence<A>;
using B0 = zero_sequence<B>;
A a; B b;
cout <<< std::is_same<A,B>::value << endl; // is false
A0 a0;
B0 b0; // < error implicit instantiation of undefined template
return 0;
}
I don't understand why the two cases are different (on Mac Clang llvm 9.0.0).
In case it is relevant to explaining this behavior, I am using the following implementation of index_sequence
and make_index_sequence
in C++11 I think I got it from here:
template<typename Integer, Integer... I>
struct integer_sequence {
using type = integer_sequence;
using value_type = Integer;
static constexpr size_t size() noexcept { return sizeof...(I); }
};
template<size_t... Ints> using index_sequence = integer_sequence<size_t, Ints...>;
template <typename,typename> struct _merge_and_renumber;
template <size_t... I1, size_t... I2>
struct _merge_and_renumber<index_sequence<I1...>,index_sequence<I2...>>
: index_sequence<I1..., (sizeof...(I1)+I2)...> {};
template <size_t N>
struct make_index_sequence
: _merge_and_renumber<typename make_index_sequence<N/2>::type,
typename make_index_sequence<N - N/2>::type> {};
template<> struct make_index_sequence<0> : index_sequence<> {};
template<> struct make_index_sequence<1> : index_sequence<0> {};
Upvotes: 1
Views: 595
Reputation: 66210
I suggest a zero_sequence
completely different: not a struct
but a using
based over a decltype()
, a std::declval()
and a only declared helper function (following the std::declval()
example.
I mean... if you define the following helper function
template <std::size_t ... Is>
constexpr auto zeHelper (index_sequence<Is...> const &)
-> decltype( index_sequence<(Is,0u)...>{} );
zero_sequence
can be defined as
template <typename T>
using zero_sequence = decltype(zeHelper(std::declval<T>()));
and you can declate both a0
and b0
A0 a0;
B0 b0; // now works
and compile without problems also
static_assert( std::is_same<A0, B0>::value, "!" );
I'm perplexed as to why this is happening [...] I don't understand why the two cases are different
The problem with your zero_sequence
template struct specialization
template<size_t...I>
struct zero_sequence<index_sequence<I...>> : index_sequence<(I*0u)...>{};
is that there is also the generic version (unimplemented, but there is)
template<typename> struct zero_sequence;
So when you use a index_sequence
as template parameter for zero_sequence
, the implemented specialization matches and is selected.
But when you use a make_index_sequence
, that inherit from index_sequence
and can be converted to a index_sequence
but isn't exactly a index_sequence
, the specialization doesn't match and the generic (unimplemented) version of zero_sequence
is selected.
Passing through a function, the zeHelper()
that I propose or something similar, avoids this problem because a make_index_sequence
is convertible to a index_sequence
.
Upvotes: 2