为什么clang无法使用默认的integer_sequence实例化嵌套的可变参数模板?

W.F*_*.F. 11 c++ templates c++14

考虑一个例子:

#include <utility>

template <class... Ts>
struct pack { 
   static constexpr std::size_t size = sizeof...(Ts);
};

template <class P, class = std::make_index_sequence<P::size>>
struct ipack;

template <class... Ts, std::size_t... Is>
struct ipack<pack<Ts...>, std::index_sequence<Is...>> { 
   static constexpr std::size_t size = sizeof...(Ts);
};

template <class IP, class = std::make_index_sequence<IP::size>>
struct vpack;

template <class... Ts, std::size_t... Is>
struct vpack<ipack<pack<Ts...>>, std::index_sequence<Is...>> { 
   static constexpr std::size_t size = sizeof...(Ts);
};

int main() {
    vpack<ipack<pack<int, int, int>>> vp;
    static_cast<void>(vp);
}
Run Code Online (Sandbox Code Playgroud)

clang 报告问题:

prog.cc:29:39: error: implicit instantiation of undefined template 'vpack<ipack<pack<int, int, int>, std::__1::integer_sequence<unsigned long, 0, 1, 2> >, std::__1::integer_sequence<unsigned long, 0, 1, 2>
vpack<ipack<pack<int, int, int>>> vp;
                                  ^
Run Code Online (Sandbox Code Playgroud)

gcc 不会在这里分享铿锵的感觉.哪个编译器是对的?以上代码是否以某种方式形成了错误?

jan*_*nde 1

我无法使用godbolt重现您的错误。Clang 和 gcc 编译它就可以了。

然而,在使用编译器时,我发现 msvc 不喜欢你的代码,因为ipack. 不过,如果您直接提供参数,它就会起作用:

template <class...Ts, std::size_t... Is>
struct vpack<ipack<pack<Ts...>,std::index_sequence<Is...>>, std::index_sequence<Is...>> { 
   static constexpr std::size_t size = sizeof...(Ts);
};
Run Code Online (Sandbox Code Playgroud)

此更改还修复了您的 clang 错误。(我不知道如何在 wandbox 中获取链接......)

编辑:

msvc 还指出了另一个错误,我在上面省略了该错误。vpack必须是可构造的。但是,因为您刚刚声明了它 ( struct vpack;),所以没有可用的默认构造函数。您可以通过使用以下命令来定义它来解决此问题struct vpack {};:这也解决了 clang 问题。(即使没有上述内容。)

编辑2:

思考为什么需要使用struct vpack {};我在代码中发现了另一个缺陷。它可以简化为:

#include <utility>

template <class... Ts>
struct pack { 
   static constexpr std::size_t size = sizeof...(Ts);
};

template <class P, class = std::make_index_sequence<P::size>>
struct ipack {};

template <class... Ts, std::size_t... Is>
struct ipack<pack<Ts...>, std::index_sequence<Is...>> { 
   static constexpr std::size_t size = sizeof...(Ts);
};

template <class IP, class = std::make_index_sequence<IP::size>>
struct vpack {};

int main() {
    vpack<ipack<pack<int, int, int>>> vp;
    static_cast<void>(vp);
}
Run Code Online (Sandbox Code Playgroud)

原因是,您的模板专业化vpack没有被实例化,因为您正在使用主模板的默认参数。(vpack<ipack<pack<int, int, int>>>仅提供一个参数,而不是专业化所需的两个参数。)

您甚至可以删除 的模板专门化ipack,如果不是因为您在vpack主实例中隐式使用它的话。(IP::size指的是 的专业化ipack。)

(我写了一个版本这样做: https: //godbolt.org/g/6Gbsvd

因此,msvc 和 wandboxes clang 拒绝编译您的代码是正确的。我不知道为什么它在 gcc 和 godbolts clang 下工作。它可能与默认参数的处理方式有关......

size有趣的是,您可以通过在主要内部定义来看到差异vpack

#include <utility>

template <class... Ts>
struct pack { 
   static constexpr std::size_t size = sizeof...(Ts);
};

template <class P, class = std::make_index_sequence<P::size>>
struct ipack;

template <class... Ts, std::size_t... Is>
struct ipack<pack<Ts...>, std::index_sequence<Is...>> { 
   static constexpr std::size_t size = sizeof...(Ts);
};

template <class IP, class = std::make_index_sequence<IP::size>>
struct vpack { static constexpr std::size_t size = 0; };

template <class... Ts, std::size_t... Is>
struct vpack<ipack<pack<Ts...>,std::make_index_sequence<sizeof...(Ts)>>, std::index_sequence<Is...>> { 
   static constexpr std::size_t size = sizeof...(Ts);
};

int main() {
    vpack<ipack<pack<int, int, int>>> vp;

    return decltype(vp)::size;
}
Run Code Online (Sandbox Code Playgroud)

gcc 和 clang 返回3,但 msvc 返回0