[英]Clang fails to compile parameter pack expansion using template metaprogramming
I have a boost::variant
of several ranges . 我有几个范围的
boost::variant
。 In this context, a range is just a std::pair<It, It>
, where It
is an iterator. 在这个上下文中,一个范围只是一个
std::pair<It, It>
, It
是一个迭代器。 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). 由于我不知道迭代器类型,我使用一些模板元编程来获取
std::pair
的first_type
,因为我需要一个包含单个迭代器的第二个boost::variant
(对应于它的一些活动元素)类型)。
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). 下面的代码被简化以帮助解决问题,但考虑到我的
RangeVariant
有一个未知数量的范围(这意味着我不能手动创建它,因为我可以为这个特殊情况做)。
#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. 上面的程序使用gcc正确编译,但是没有使用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. 所以,似乎clang试图获取
boost::detail::variant::void_
的first_type
,但不知何故gcc识别它并忽略它。 Something similar happens if I obtain the type for the first element using the <tuple>
header: 如果我使用
<tuple>
标头获取第一个元素的类型,会发生类似的事情:
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_
: 此更改后的错误是不同的,但再次与clang尝试将操作应用于
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. 我正在使用boost 1.57.0,gcc 4.8.3和clang 3.6.0,总是使用
-std=c++11
和-Wall -Werror -Wextra
标志。 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.
如果我的用法不正确,我甚至不知道这是clang或boost中的错误,甚至是gcc中的错误。 Thanks in advance for your help.
在此先感谢您的帮助。
We agree on that void_
is part of boost::variant
's pre-variadic template workaround (every instantiation is boost::variant<MandatoryType, ⟪boost::detail::variant::void_ ⨉ 𝖫𝖨𝖲𝖳_𝖲𝖨𝖹𝖤 ⟫>
). 我们同意
void_
是boost::variant
前模板解决方法的一部分(每个实例化都是boost::variant<MandatoryType, ⟪boost::detail::variant::void_ ⨉ 𝖫𝖨𝖲𝖳_𝖲𝖨𝖹𝖤 ⟫>
)。
Now, the thing is that using metashell I found out there exists at least one version of boost::variant
that does not use this workaround. 现在,问题是使用metashell我发现存在至少一个版本的
boost::variant
,它不使用这种解决方法。
Looking around, I found that there was a bug recently fixed about how boost libs do not recognize clang's variadic template capability correctly. 环顾四周,我发现最近有一个错误修复了如何提升libs无法正确识别clang的可变参数模板功能。
To answer your question: gcc compiles because boost libs recognize variadic template availability, while missing clang's. 回答你的问题:gcc编译,因为boost libs识别可变参数模板的可用性,同时缺少clang's。 This results in
void_
failing to be instantiate in your meta-programming tangle as this struct
has been declared, but not defined. 这导致
void_
无法在元编程纠结中实例化,因为此struct
已被声明,但未定义。
The reason this doesn't work is that boost::variant
isn't implemented the way you think it is. 这不起作用的原因是
boost::variant
没有像你想象的那样实现。
boost::variant
like all of boost is compatible with C++03, before the time when there were variadic templates. 在存在可变参数模板之前,
boost::variant
就像所有的boost都与C ++ 03兼容。
As a result, boost::variant
has to work around the lack of that language feature, by imposing a maximum number of variants and using only C++03 template features. 因此,
boost::variant
必须通过强加最大数量的变体并仅使用C ++ 03模板功能来解决缺少该语言功能的问题。
The way they do this is, the template has 20 template arguments, and they all have a default value of boost::variant::detail::void_
. 他们这样做的方式是,模板有20个模板参数,它们都有一个默认值
boost::variant::detail::void_
。
Your variadic capture is catching those extra parameters, just the same way that if you tried to capture all the parameters to std::vector
you would get your type, also an allocator, etc., even if you didn't explicitly specify an allocator. 你的可变参数捕获正在捕获那些额外的参数,就像你试图捕获
std::vector
所有参数一样,你会得到你的类型,也是一个分配器等,即使你没有明确指定一个分配器。
The work arounds I can think of off-hand are, 我能想到的工作是,
1) Don't use boost::variant
, use a C++11 variant based on variadic templates. 1)不要使用
boost::variant
,使用基于可变参数模板的C ++ 11变体。 There are many implementations floating around. 有许多实现浮动。
2) Use boost variant, but also create a type-trait that permits you recover the original parameter pack from a typelist. 2)使用boost变体,但也创建一个类型特征,允许您从类型列表中恢复原始参数包。 You would have to make sure that every time you instantiate it, you also create an entry in the type trait, but you can use a macro to make sure that happens.
您必须确保每次实例化它时,您还在类型特征中创建一个条目,但您可以使用宏来确保发生这种情况。
3) There may be a way to make boost::variant
use an implementation based on variadic templates? 3)有可能有一种方法可以使
boost::variant
使用基于可变参数模板的实现吗? But I'm not sure of this, I would have to review the docs. 但我不确定这一点,我将不得不审查文档。 If there is then it means there is some preprocessor define you can use to force this.
如果有,则表示存在一些预处理器定义,您可以使用它来强制执行此操作。
Edit: The macro is this actually: http://www.boost.org/doc/libs/1_60_0/doc/html/BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES.html 编辑:宏实际上是这样的: http : //www.boost.org/doc/libs/1_60_0/doc/html/BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES.html
So in recent versions of boost, you have to request explicitly not to have the variadic implementation, unless you are on C++03 presumably? 所以在最近的boost版本中,你必须明确要求不具有可变参数的实现,除非你大概是在C ++ 03上吗?
You might want to explicitly check if something in one of your headers is defining this for some reason. 您可能希望明确检查某个标题中的某些内容是否因某种原因而定义了此内容。
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. 尽管克里斯和费利佩的贡献都部分回答了我的问题(谢谢大家!),这里有一个更新,实际上编译了我提到的Boost和clang版本。
First, update the specialization of FirstTypeVariant
so that it obtains the type from another structure instead of directly obtaining T::first_type
: 首先,更新
FirstTypeVariant
以便从另一个结构获取类型,而不是直接获取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). 然后,专门化
ObtainFirstType
结构,以便它返回std::pair<T, T>
的迭代器类型(请记住,在我的用例中, T
是一个迭代器)。
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.
具有clang的变体的元素数量将始终为20,因此任何依赖于该变量的算法都可能会改变其行为。 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: 在我的例子中,我创建了一个包含3个元素的
variant
,然后我计算了它:
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 GCC的输出是
RangeVariant size : 3 IteratorVariant size : 3
while the output with CLANG is the following: 而CLANG的输出如下:
RangeVariant size : 20 IteratorVariant size : 20
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.