Pack expansion of pack of different length error in clang, compiles in gcc. Who is right?

Viewed 138

I was working on a tuple type which uses multiple inheritance instead of the classic recursive definition. While doing so I ran into a strange problem when expanding multiple parameter packs which according to clang have different lengths, whereas gcc compiles the code without problems.

A small example demonstrating the problem can be found here: https://godbolt.org/z/oKbYKd9je

When compiled with clang 12.0.1 I get the error:

pack expansion contains parameter pack 'Ts' that has a different length (3 vs. 1) from outer parameter packs

When switching to gcc 11, the code compiles without problems. I'm wondering which compiler is correct?. To me it seems that this should just work and that the bug is in clang.


Code also included here, just in case the external link expires:

#include <type_traits>
#include <utility>

template <size_t index, typename T>
struct element_holder {
  T value;
};

template <typename... Ts>
struct tuple : public Ts... {};

namespace detail{
 
    template<typename T>
    struct make_tuple_impl2;

    template<size_t...Is>
    struct make_tuple_impl2<std::index_sequence<Is...>>{
        template<typename ...Ts>
        using f = tuple<element_holder<Is, Ts>...>; //<-- error occurs here
    };

    template<size_t n>
    struct make_tuple_impl{
        template<typename... Ts>
        using f=typename make_tuple_impl2<std::make_index_sequence<n>>::template f<Ts...>;
    };
}

struct make_tuple{
    template<typename ...Ts>
    // This does not work with clang 12.0.1, but does with gcc 11
    using f = typename detail::make_tuple_impl<sizeof...(Ts)>::template f<Ts...>;
    //This works:
    //using f=typename detail::make_tuple_impl2<std::make_index_sequence<sizeof...(Ts)>>::template f<Ts...>;
};


int main() {
using tuple_t = typename make_tuple::template f<int, int, bool>;
}
1 Answers

Not a direct answer to the question, but if someone lands here looking for a workaround, here is an alternative:

As long as both parameter packs are in the same template declaration, it works fine on both compilers.

Here's what that would look like for OP's code:

namespace detail{
 
    template<typename... T>
    struct make_tuple_impl2;

    template<size_t...Is, typename... Ts>
    struct make_tuple_impl2<std::index_sequence<Is...>, Ts...>{
        using type = tuple<element_holder<Is, Ts>...>;
    };

    template<size_t n>
    struct make_tuple_impl{
        template<typename... Ts>
        using f=typename make_tuple_impl2<std::make_index_sequence<n>, Ts...>::type;
    };
}
Related