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

时间:2017-04-18 10:37:36

标签: 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);
}

clang reports问题:

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

gcc does not share在这里感受到了感情。哪个编译器是对的?以上代码是否以某种方式形成错误?

1 个答案:

答案 0 :(得分:1)

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

但是,在使用编译器时,我发现由于ipack中的默认参数,msvc不喜欢您的代码。不过,如果您直接提供参数,它将起作用:

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

此更改也修复了您的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);
}

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

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

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

因此,msvc和wandboxes叮当在拒绝编译代码时是正确的。我不确定为什么它可以在gcc和godbols 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;
}

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