Clang无法使用模板元编程来编译参数包扩展

时间:2016-03-18 21:09:42

标签: c++ clang metaprogramming template-meta-programming boost-variant

我有几个范围的QApplication::restoreOverrideCursor() 。在此上下文中,范围只是boost::variant,其中std::pair<It, It>是迭代器。我用它来存储满足某些属性的迭代器范围。

由于我不知道迭代器类型,我使用一些模板元编程来获取It的{​​{1}},因为我需要第二个first_type单个迭代器(对应于该类型的某些活动元素)。

以下代码已经过简化以帮助解决问题,但请注意我的std::pair中有一个未知数量的范围(这意味着我无法手动创建它,因为我可以为此做特殊情况)。

boost::variant

上面的程序使用gcc正确编译,但是因为clang失败了。我得到的错误如下:

RangeVariant

所以,似乎clang试图获得#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>; }; 的{​​{1}},但不知何故gcc认出它并忽略它。如果我使用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_; ^ 标题获取第一个元素的类型,则会发生类似的事情:

first_type

此更改后的错误不同,但又与clang尝试将操作应用于boost::detail::variant::void_有关:

<tuple>

我使用boost 1.57.0,gcc 4.8.3和clang 3.6.0,总是使用带有using type = B<typename std::tuple_element<0, Pair>::type...>; 标志的boost::detail::variant::void_。使用其中任何一个的其他版本都不是一个选择: - (

任何帮助将不胜感激。如果我的用法不正确,我甚至不知道这是clang或boost中的错误,甚至是gcc中的错误。在此先感谢您的帮助。

3 个答案:

答案 0 :(得分:3)

我们同意void_boost::variant的变量前模板解决方法的一部分(每个实例都是boost::variant<MandatoryType, ⟪boost::detail::variant::void_ ⨉ _ ⟫>)。

现在,问题是使用metashell我发现存在至少一个版本的boost::variant 使用此解决方法。

环顾四周,我发现有一个bug recently fixed关于boost libs如何无法正确识别clang的可变参数模板功能。

回答你的问题:gcc编译,因为boost libs识别可变参数模板的可用性,同时缺少clang's。这导致void_无法在元编程纠结中实例化,因为已声明此struct,但未定义。

答案 1 :(得分:2)

这不起作用的原因是boost::variant没有按照您的想法实施。

boost::variant就像所有的boost都与C ++ 03兼容,在有可变参数模板的时候。

因此,boost::variant必须通过强加最大数量的变体并仅使用C ++ 03模板功能来解决缺少该语言功能的问题。

他们这样做的方式是,模板有20个模板参数,它们的默认值都是boost::variant::detail::void_

你的可变参数捕获正在捕获那些额外的参数,就像你试图捕获所有参数到std::vector你会得到你的类型,也是一个分配器等,即使你没有&#39 ; t显式指定分配器。

我能想到的工作是,

1)不要使用boost::variant,使用基于可变参数模板的C ++ 11变体。有许多实现浮动。

2)使用boost变体,但也创建一个类型特征,允许您从类型列表中恢复原始参数包。您必须确保每次实例化它时,您还在类型特征中创建一个条目,但您可以使用宏来确保它发生。

3)可能是一种让boost::variant使用基于可变参数模板的实现的方法吗?但我不确定这一点,我将不得不审查文档。如果有,则意味着有一些预处理器定义,您可以使用它来强制执行此操作。

编辑:宏实际上就是这样: http://www.boost.org/doc/libs/1_60_0/doc/html/BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES.html

所以在最近的boost版本中,你必须明确要求不要使用可变参数,除非你大概是在C ++ 03上?

您可能希望明确检查某个标题中的某些内容是否因某种原因而定义了此内容。

答案 2 :(得分:-1)

尽管Read about Java's heap sizeChris'贡献部分回答了我的问题(谢谢大家!),这里有一个实际编译我提到的Boost和clang版本的更新。

首先,更新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...>;
};

然后,专门化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;
};

现在,这将编译和工作,但有一个警告。具有clang的变体的元素数量将始终为20,因此任何依赖于该变量的算法都可能会改变其行为。我们可以这样算:

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

在我的示例中,我创建了一个包含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;
};

GCC的输出是

RangeVariant size    : 3
IteratorVariant size : 3

而CLANG的输出如下:

RangeVariant size    : 20
IteratorVariant size : 20