使用C ++模板元编程表示一组类型

时间:2017-05-09 13:06:45

标签: c++ c++11 c++14 variadic-templates template-meta-programming

如何编写模板meta_set<class...Args>,使得meta_set :: type`对于Ts的所有排列都相同?

换句话说,无论顺序如何,每当参数列表相同时,我们都希望拥有相同的meta_set<>::type,也就是说,当被视为一个集合时(或者如果它更容易,则为多集) 。

例如,

std::is_same< meta_set<int,double,string>::type, meta_set<double,string,int>::type  >::value == true
std::is_same< meta_set<int,double,string>::type, meta_set<double,string,bool>::type  >::value == false

如果您希望每个模板参数 set 对模板进行一次实例化,这可能会很方便。

注意:这不是一个家庭作业,但我在工作时处理模板繁重的代码时感到好奇。我不是元编程专家,所以我想也许人们可以分享他们的知识。

3 个答案:

答案 0 :(得分:4)

在编译时无法对所有类型进行全局排序;对typeid(T).before(typeid(U))之类的内容的访问权限不是constexpr。因此,您无法将两个meta_set<A,B>meta_set<B,A>设为同一类型,因为您无法排序。

如果类型不同,则无法修改std::is_same的行为以返回true。任何试图这样做的代码(例如通过专门化std::is_same)都会违反std::is_same的要求,这会使程序格式错误,无需诊断。

如果您将类型集限制为所有类型的某个子集,则可以执行此操作。最简单的方法是拥有一个集中的列表:

template<class...T>
struct types_t {};

using global_order = types_t<int, double, std::string>;

然后,通过一些模板元编程,您可以获得global_order中的类型索引,然后基于此编写类型分类器。

然后

template<class Types>
struct type_sorter;

template<class...Ts>
struct type_sorter<types_t<Ts...>> {
  // todo
  using type=types_t< result_somehow >;
};

一旦写完:

template<class...Ts>
using meta_set = typename type_sorter< types_t<Ts...> >::type;

会起作用。

关于如何在编译时使用模板堆栈溢出对东西进行排序可能有解决方案。就个人而言,我发现合并排序最容易写入所有n log(n)种类的模板元编程。上次它做了它需要大约100多行密集模板代码?包括编写TMP库。但是,现在存在TMP库,甚至可能为您预先编写了类型排序代码。

现在,单个全局排序是最简单的事情。我们可以通过教授关于模板,排序模板的类型分类器,然后按照它们的组成类型和值等对所述模板实例进行排序来使它变得更强大。

这很难实现,它需要为每个模板,模板类型(所以template<class...>class vs template<class, std::size_t>class)和基本类型(intfloatstruct foo)支持。

这将是很多工作。

编写自定义特征会稍微容易一些

template<class Lhs, class Rhs>
struct smart_is_same;

会像std::is_same一样,除非有人meta_set s检查他们是否有相同的内容而不是寻找严格的类型相等。但是,您的评论提到这不是您的实际问题,而是您正在讨论将meta_set参数传递给不同的模板,并希望它们按规范顺序排列。

答案 1 :(得分:1)

规范地表示一组类型的唯一可想到的方式是使用排序列表(或者可以转换为排序列表的东西,例如二叉搜索树)。但是C ++中的类型之间没有自然的编译时排序,所以没有什么能按排序

如果C ++需要std::type_info::beforeconstexpr,或者定义具有类似功能的模板,比如std::is_before<typename A, typename B>,它会在类型之间创建一个全局静态排序,这将创建规范排序的列表可能。可悲的是,事实并非如此。

缺乏编译器支持的排序,程序员必须为每个可能的类型对定义自己的is_before<A,B>,这对于每个对类型当然是不可能的,但仅适用于某些类型有限的已知预设集。

答案 2 :(得分:0)

我的想法不是对类型进行排序,而只是检查第一个元集中的类型是否出现在第二个元集中。

template <typename... Args>
struct TypeList;

template <typename Head, typename... Tail>
struct TypeList<Head, Tail...>
{
    using TailList = TypeList<Tail...>;
    using HeadType = Head;

    static_assert(!TailList::template contains<Head>(), "Types must be unique");

    static constexpr int size()
    {
        return 1 + TailList::size();
    }

    template <typename Type>
    static constexpr bool contains()
    {
        return std::is_same<Head, Type>::value || TailList::template contains<Type>();
    }
};

template<>
struct TypeList<>
{
    static constexpr int size()
    {
        return 0;
    }

    template <typename Type>
    static constexpr bool contains()
    {
        return false;
    }
};


template <typename ListLhs, typename ListRhs>
struct IsSame
{
    static constexpr bool value()
    {
        return ListLhs::size() == ListRhs::size() && valueImpl();
    }

    static constexpr bool valueImpl()
    {
        return ListLhs::template contains<typename ListRhs::HeadType>() &&
               IsSame<ListLhs,typename ListRhs::TailList>::valueImpl();
    }
};

template <typename ListLhs>
struct IsSame<ListLhs, TypeList<>>
{
    static constexpr bool value()
    {
        return false;
    }

    static constexpr bool valueImpl()
    {
        return true;
    }
};

template <>
struct IsSame<TypeList<>, TypeList<>>
{
    static constexpr bool value()
    {
        return true;
    }

    static constexpr bool valueImpl()
    {
        return false;
    }
};

struct MyStruct{};

using Types = TypeList<int, bool, char, MyStruct, double>;
using TypesSame = TypeList<int, MyStruct, bool, char, double>;
using LessTypes = TypeList<int, bool, char>;
using EmptyTypes = TypeList<>;

static_assert(IsSame<Types, TypesSame>::value(), "Underlying types should be the same");
static_assert(!IsSame<Types, LessTypes>::value(), "Less types");
static_assert(!IsSame<Types, EmptyTypes>::value(), "Not the same as Empty");
static_assert(IsSame<EmptyTypes, EmptyTypes>::value(), "Empty types");