【问题标题】:Pack expansion of pack of different length error in clang, compiles in gcc. Who is right?在clang中不同长度包的包扩展错误,在gcc中编译。谁是对的?
【发布时间】:2021-08-10 12:29:34
【问题描述】:

我正在研究一种使用多重继承而不是经典递归定义的元组类型。 这样做时,我在扩展多个根据clang具有不同长度的参数包时遇到了一个奇怪的问题,而gcc编译代码没有问题。

可以在此处找到一个演示问题的小示例: https://godbolt.org/z/oKbYKd9je

使用 clang 12.0.1 编译时出现错误:

包扩展包含具有不同的参数包“Ts” 来自外部参数包的长度(3 vs. 1)

当切换到 gcc 11 时,代码编译没有问题。 我想知道哪个编译器是正确的?对我来说,这似乎应该可以正常工作,并且该错误已存在。


此处也包含代码,以防外部链接过期:

#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>;
}

【问题讨论】:

标签: c++ gcc clang variadic-templates


【解决方案1】:

不是对这个问题的直接回答,但如果有人来到这里寻找解决方法,这里有一个替代方案:

只要两个参数包在同一个模板声明中,它就可以在两个编译器上正常工作。

这是 OP 代码的样子:

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;
    };
}

【讨论】:

  • 感谢您的加入。我不想使用这种解决方法的主要原因是因为我试图最小化在更大的编译时系统中实例化的不同类型的数量,该系统使用这些元组作为构建块(模板别名要快得多,所以使用这些有助于编译时间)。您的评论“只要两个参数包都在同一个模板声明中,它就可以正常工作”确实让我想知道标准中是否指定了这些内容(也许 gcc 是错误的,代码确实应该给出错误?)
  • @Antiro42 如果目标是最小化中间类型的数量,您可以在满足所有编译器的同时将其降低到一个额外的类型,如下所示:godbolt.org/z/KWqsz7oeY
  • 我想将存储在元组中的类型排除在类型模板参数之外,因为我将创建许多相同大小但存储不同类型的不同元组。如果我只将索引作为类型模板参数,则不同元组的模板别名会有所不同,因此在创建更多相同大小的元组时不会实例化其他类型(即使在存储新的类型组合时也是如此)。
  • @Antiro42 哦,对不起,关于那个,你可以通过稍微改变一下来取回这个:godbolt.org/z/8TEPcx9Tr
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多