5

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>;
}
Antiro42
  • 177
  • 8
  • Does this answer your question? [Can C++ struct static member variables shadow non type template parameters?](https://stackoverflow.com/questions/68712964/can-c-struct-static-member-variables-shadow-non-type-template-parameters) – Mgetz Aug 10 '21 at 12:45
  • 3
    Maybe relevant? [Clang fails to expand parameter pack in std::function instantiation](https://stackoverflow.com/questions/57080425/clang-fails-to-expand-parameter-pack-in-stdfunction-instantiation) . Strikes me as a bug - clang fails to expand `Is` pack. – Quimby Aug 10 '21 at 12:47
  • Does this answer your question? [Clang fails to expand parameter pack in std::function instantiation](https://stackoverflow.com/questions/57080425/clang-fails-to-expand-parameter-pack-in-stdfunction-instantiation) – Gambit1614 Aug 11 '21 at 08:10
  • @Gambit1614 It sounds like it might be related, but I honestly don't know. From the error I get it sounds like it is indeed not expanding Is, but I don't know if the reason it is not expending Is is the same as in the other question. Perhaps someone with more knowledge about dependent types and the way they are treated by the compiler can give some more insights here. – Antiro42 Aug 11 '21 at 11:55

1 Answers1

2

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;
    };
}
  • Thanks for the addition. The main reason I do not want to use such a workaround is because I'm trying to minimize the number of different types that are instantiated in a bigger compile time system that uses these tuples as building blocks (template aliases are significantly faster, so using those helps with compile time). Your remark "As long as both parameter packs are in the same template declaration, it works fine" does make me wonder if something along those lines is specified in the standard (maybe gcc is wrong and the code should indeed give an error?) – Antiro42 Aug 10 '21 at 14:13
  • @Antiro42 If the objective is to minimize the number of intermediate types, You can bring it down to a single extra type while satisfying all compilers like so: https://godbolt.org/z/KWqsz7oeY –  Aug 10 '21 at 15:17
  • I'd like to keep the types stored in the tuple out of the type template parameters as I'll be creating many different tuples of the same size, but storing different types. If I only have the indices as type template parameters only the template aliases will be different for the different tuples and as such no additional types will be instantiated when creating further tuples of the same size (even when storing a new combination of types). – Antiro42 Aug 11 '21 at 11:45
  • @Antiro42 Oh sorry, about that, you can get this back by shuffling things around a bit: https://godbolt.org/z/8TEPcx9Tr –  Aug 11 '21 at 12:48