Clang fails to compile parameter pack expansion using template metaprogramming

Viewed 348

I have a boost::variant of several ranges. In this context, a range is just a std::pair<It, It>, where It is an iterator. I use this to store ranges of iterators satisfying certain properties.

Since I don't know the iterator types, I use a little template meta-programming to obtain the first_type of the std::pair, since I need a second boost::variant containing a single iterator (corresponding to some active element of that type).

The following code is simplified to help with the question, but consider that I have an unknown number of ranges in my RangeVariant (which means I can't create it manually, as I can do for this particular case).

#include <utility>
#include <vector>

#include <boost/variant.hpp>

template <class A, template <typename...> class B>
struct FirstTypeVariantImpl;

template <template <typename...> class A, typename... Pair, template <typename...> class B>
struct FirstTypeVariantImpl<A<Pair...>, B> /*! specialization */
{
    using type = B<typename Pair::first_type...>;
};

template <class A, template <typename...> class B>
using FirstTypeVariant = typename FirstTypeVariantImpl<A, B>::type;

int main()
{
    using Container = std::vector<int>;
    using Range = std::pair<Container::iterator, Container::iterator>;
    using RangeVariant = boost::variant<Range>;
    using IteratorVariant = FirstTypeVariant<RangeVariant, boost::variant>;
};

The above program compiles correctly with gcc, but fails with clang. The error I get is the following:

program.cpp:12:29: error: incomplete type 'boost::detail::variant::void_' named in nested name specifier
using type = B<typename Pair::first_type...>;
                        ^~~~~~
program.cpp:16:1: note: in instantiation of template class 'FirstTypeVariantImpl<boost::variant<std::pair<__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > >, boost::detail::variant::void_, ..., boost::detail::variant::void_>, variant>' requested here
using FirstTypeVariant = typename FirstTypeVariantImpl<A, B>::type;
^
program.cpp:23:29: note: in instantiation of template type alias 'FirstTypeVariant' requested here
using IteratorVariant = FirstTypeVariant<RangeVariant, boost::variant>;
                        ^
../../../include/boost/variant/variant_fwd.hpp:193:8: note: forward declaration of 'boost::detail::variant::void_'
struct void_;
       ^

So, it seems clang is attempting to obtain the first_type of boost::detail::variant::void_, but somehow gcc recognizes it and ignores it. Something similar happens if I obtain the type for the first element using the <tuple> header:

using type = B<typename std::tuple_element<0, Pair>::type...>;

The error after this change is different, but again related to clang trying to apply the operation to boost::detail::variant::void_:

program.cpp:13:34: error: implicit instantiation of undefined template 'std::tuple_element<0, boost::detail::variant::void_>'
using type = B<typename std::tuple_element<0, Pair>::type...>;

I'm using boost 1.57.0, gcc 4.8.3 and clang 3.6.0, always using -std=c++11 with -Wall -Werror -Wextra flags. Using other versions of either of these is not an option :-(

Any help would be appreciated. I don't even know whether this is a bug in clang or boost, or even in gcc, if my usage is incorrect. Thanks in advance for your help.

3 Answers
Related