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.
Although both Chris' and Felipe's contributions answer my question partially (thanks guys!), here is an update that actually compiles with the Boost and clang versions I mentioned.
First, update the specialization of FirstTypeVariant
so that it obtains the type from another structure instead of directly obtaining T::first_type
:
template <template <typename...> class A, typename... Pair, template <typename...> class B>
struct FirstTypeVariantImpl<A<Pair...>, B> /*! specialization */
{
using type = B<typename ObtainFirstType<Pair>::type...>;
};
Then, specialize the ObtainFirstType
struct so that it returns the iterator type for std::pair<T, T>
(remember that in my use case, T
is an iterator).
template <typename T>
struct ObtainFirstType
{
using type = T;
};
template <typename T>
struct ObtainFirstType<std::pair<T, T>>
{
using type = T;
};
Now, this will compile and work, but there's a caveat. The number of elements of the variant with clang will always be 20, so any algorithm depending on that might change its behavior. We can count them like this:
template <typename... Ts>
struct VariantSize
{
static constexpr std::size_t size = 0;
};
template <typename... Ts>
struct VariantSize<boost::variant<Ts...>>
{
static constexpr std::size_t size = sizeof...(Ts);
};
In my example, I created a variant
with 3 elements, which I then counted:
int main()
{
using ContainerA = std::vector<int>;
using ContainerB = std::vector<double>;
using ContainerC = std::vector<bool>;
using RangeA = std::pair<ContainerA::iterator, ContainerA::iterator>;
using RangeB = std::pair<ContainerB::iterator, ContainerB::iterator>;
using RangeC = std::pair<ContainerC::iterator, ContainerC::iterator>;
using RangeVariant = boost::variant<RangeA, RangeB, RangeC>;
using IteratorVariant = FirstTypeVariant<RangeVariant, boost::variant>;
std::cout << "RangeVariant size : " << std::to_string(VariantSize<RangeVariant>::size) << std::endl;
std::cout << "IteratorVariant size : " << std::to_string(VariantSize<IteratorVariant>::size) << std::endl;
};
The output with GCC is
RangeVariant size : 3 IteratorVariant size : 3
while the output with CLANG is the following:
RangeVariant size : 20 IteratorVariant size : 20
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments