繁体   English   中英

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

[英]Clang fails to compile parameter pack expansion using template metaprogramming

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

由于我不知道迭代器类型,我使用一些模板元编程来获取std::pairfirst_type ,因为我需要一个包含单个迭代器的第二个boost::variant (对应于它的一些活动元素)类型)。

下面的代码被简化以帮助解决问题,但考虑到我的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>;
};

上面的程序使用gcc正确编译,但是没有使用clang。 我得到的错误如下:

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_;
       ^

所以,似乎clang试图获取boost::detail::variant::void_first_type ,但不知何故gcc识别它并忽略它。 如果我使用<tuple>标头获取第一个元素的类型,会发生类似的事情:

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

此更改后的错误是不同的,但再次与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...>;

我正在使用boost 1.57.0,gcc 4.8.3和clang 3.6.0,总是使用-std=c++11-Wall -Werror -Wextra标志。 使用其中任何一个的其他版本不是一个选项:-(

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

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

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

环顾四周,我发现最近有一个错误修复了如何提升libs无法正确识别clang的可变参数模板功能。

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

这不起作用的原因是boost::variant没有像你想象的那样实现。

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

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

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

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

我能想到的工作是,

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上吗?

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

尽管克里斯费利佩的贡献都部分回答了我的问题(谢谢大家!),这里有一个更新,实际上编译了我提到的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 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM