Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why clang is not able to instantiate nested variadic template with defaulted integer_sequence?

Consider an example:

#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 a problem with:

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 clangs feelings here. Which compiler is right? Is the code above ill-formed in some way?

like image 687
W.F. Avatar asked Apr 18 '17 10:04

W.F.


1 Answers

I cannot reproduce your error using godbolt. Clang and gcc compile it just fine.

However, playing around with the compilers, I found that msvc doesn't like your code, because of the default parameter in ipack. Though, if you supply the parameter directly, it works:

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

This change fixes your clang error as well. (I couldn't figure out how to get a link in wandbox...)

Edit:

There is another error msvc is pointing out, which I omitted above. vpack has to be constructible. However, because you just declared it (struct vpack;), there is no default constructor available. You can fix this by defining it, using: struct vpack {};. That solves the clang problem as well. (Even without the above.)

Edit 2:

Thinking about why you need to use struct vpack {}; I found another flaw in your code. It can be reduced to:

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

The reason is, that your template specialization of vpack does not get instantiated, is because you are using the default argument of the primary template. (vpack<ipack<pack<int, int, int>>> only supplies one argument instead of the two needed by the specialization.)

You could even remove the template specialization for ipack, were it not for the fact, that you are implicitly using it in the vpack primary. (IP::size refers to the specialization of ipack.)

(I wrote a version doing that: https://godbolt.org/g/6Gbsvd )

Thus, msvc and wandboxes clang were right in refusing to compile your code. I'm not sure why it works under gcc and godbolts clang. It probably has to do something with the way default parameters are handled...

Interestingly, you can see the difference by defining size inside the vpack primary:

#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 and clang return 3, but msvc returns 0.

like image 180
jan.sende Avatar answered Nov 14 '22 20:11

jan.sende