【问题标题】:How to store a type for later comparison如何存储类型以供以后比较
【发布时间】:2014-07-08 01:48:54
【问题描述】:

首先是我的用例,因为我可能认为方向错误:我想创建一个将值映射到类型的映射。比如:

Map<std::string> map;
map.insert<int, double, char>("Hey");
auto string = map.at<int, double, char>();

使用std::type_index 就很容易做到这一点。但是,当它们可转换时,我想添加匹配与搜索的类型不完全相同的类型的可能性。所以下面也应该返回"Hey",因为float可以转换成double

auto string = map.at<int, float, char>();

我不能在这种情况下使用type_index,因为std::is_convertible 只能直接用于类型。 This would be the version without conversion,但就其而言,在不进行重大更改的情况下添加转换处理似乎并不容易。


我当前的尝试类似于以下内容,请注意这不起作用,只是显示了我尝试实现的内容:

template<typename T> 
class Map {
    T value; 
    std::vector<Map<T>> children; // all the children of the current node.
                                  // in the above example, if this was
                                  // the int node, the only child 
                                  // would be the double node

    template<typename T1>
    constexpr bool is_convertible() const {
        return std::is_convertible<__T__, T1>::value; // this isn't applicable
                                                  // since __T__ can't be
                                                  // stored (this nodes
                                                  // type)
    }

public:
    template<typename T1, typename... Tn>
    void insert(T&& value) {
        // iterate through/create the child nodes until the last template param
    }
    template<typename T1, typename... Tn>
    T& at() {
        // iterate through thechild nodes until a matching child is found
        // either exact match or a convertible 

        for(auto &c: children) {
            // if the above function would work
            if(c.template is_convertible<T1>()) { 
                return c.template at<Tn...>();
            }
        }
    }

}

现在我不知道如何实现这一目标。我想将 lambdas 实现为比较器函数,但是虽然 lambda 可以存储当前节点的类型,但它不能在调用时接受模板参数进行比较。

是否有一些 C+1y 通用 lambda 比较器魔法,甚至更简单的方法?

【问题讨论】:

  • 键值对是在运行时创建还是仅在编译时创建?你到底想用这个来完成什么?
  • 如果我理解正确,那么这个想法取决于虚拟模板函数,在这种情况下实际上是不可用的。
  • @Svalorzen 在编译时。
  • 虽然我对c.template get&lt;Tn...&gt;(value) 行完全感到困惑,但这与我的理解相矛盾。这个问题毫无意义。
  • 你不能只创建一个模板结构映射,它接受可变数量的模板参数,然后预先专门化你可能想要的任何组合吗?我必须坚持,如果你想用这个来解决一些真正的问题,你可能会走错路。

标签: c++ c++11 types lambda c++14


【解决方案1】:

我希望这能满足您的需求,有足够的空间进行扩展和创建附加到您想要的任何类型组合的模板特化。不算特别漂亮,但大概可以重构一下,美化一下。

#include <iostream>

template <typename... Args>
struct map {
};

template <>
struct map<int, float, char> {
    static constexpr char value[] = "int float char";
};
constexpr char map<int,float,char>::value[];

template <typename T>
struct map<int, T> {
    static constexpr typename std::enable_if<std::is_integral<T>::value, char>::type value[] = "int, T";
};
template <typename T>
constexpr typename std::enable_if<std::is_integral<T>::value, char>::type map<int,T>::value[];

int main() {
    std::string v  = map<int,float,char>::value;
    std::string w  = map<int,int>::value;
    std::string w2 = map<int,unsigned>::value;
    // std::string w3 = map<int,float>::value; Won't compile

    std::cout << v << "\n";
    std::cout << w << "\n";
    std::cout << w2 << "\n";
    return 0;
}

【讨论】:

  • 不幸的是,这不适用于这种情况,能够通过方法调用(我的代码中的insert)添加值很重要。
  • @Appleshell 你说键是在编译时添加的。另外为什么?您正在陈述一个非常模糊的问题,其中包含许多隐藏的要求。您也许应该对此进行更多说明,以便可以充分提出解决方案。
  • 是的,我理解错了,我真的很抱歉。如果在编译时已知类型,我将其视为一个问题,从而使存储的 lambdas 之类的东西变得可行或与此相关,我想我用我在问题下的最后评论纠正了自己。我希望通过添加的附加代码使我的意图更容易理解。
【解决方案2】:

我使用 boost::fusion 编写了一些奇怪的代码,接近于做你想做的事:

#include <boost/fusion/container/map.hpp>
#include <boost/fusion/include/insert.hpp>
#include <boost/fusion/include/pair.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <string>
#include <iostream>
#include <tuple>
#include <type_traits>
#include <memory>
template <std::size_t Value1, std::size_t Value2>
struct MinSizeT {
    static const std::size_t value = (Value1 > Value2) ? Value2 : Value1;
};
template<typename T1, typename T2, std::size_t N>
struct TupleIsConvertibleHelper {
    static const bool value = std::is_convertible<typename std::tuple_element<N - 1, T1>::type, typename std::tuple_element<N - 1, T2>::type>::value && TupleIsConvertibleHelper<T1, T2, N - 1>::value;
};
template<typename T1, typename T2>
struct TupleIsConvertibleHelper<T1, T2, 0> {
    static const bool value = true;
};
template<typename T1, typename T2>
bool TupleIsConvertible() { // Return true if all types in T1 are convertible to their corresponding type in T2
    if (std::tuple_size<T1>::value != std::tuple_size<T2>::value)
        return false;
    constexpr std::size_t minSize = MinSizeT<std::tuple_size<T1>::value, std::tuple_size<T2>::value>::value;
    return TupleIsConvertibleHelper<T1, T2, minSize>::value;
}
template<typename MapInserter>
class Map {
    MapInserter mc;
    template<typename... Types>
    struct do_at {
        template <typename T>
        void operator()(T const& x) const { // Find an exact match or the last convertible match
            typedef std::tuple<Types...> t1;
            typedef typename T::first_type t2;
            if (exactMatch)
                return;
            if (std::is_same<t1, t2>::value) {
                exactMatch = true;
                value = x.second;
            }
            else if (TupleIsConvertible<t1, t2>())
                value = x.second;
        }
        mutable bool exactMatch;
        mutable typename MapInserter::value_type value;
        do_at() : exactMatch(false) {}
    };
public:
    Map(MapInserter _mc) : mc(_mc) { }
    template<typename... Types>
    typename MapInserter::value_type at() {
        do_at<Types...> res;
        boost::fusion::for_each(mc.data->map, res);
        return res.value;
    }
};
template<typename ValueType, typename MapType = boost::fusion::map<>, typename ParentType = void*>
struct MapInserter {
    typedef ValueType value_type;
    struct Helper {
        MapType map;
        std::shared_ptr<ParentType> parent; // Must keep parent alive because fusion is lazy.
        Helper() = default;
        Helper(MapType&& _map, std::shared_ptr<ParentType> _parent) : map(std::move(_map)), parent(_parent) {}
    };
    std::shared_ptr<Helper> data;
    template<typename... KeyTypes>
    auto Insert(ValueType value) -> MapInserter<ValueType, decltype(boost::fusion::insert(data->map, boost::fusion::end(data->map), boost::fusion::make_pair<std::tuple<KeyTypes...>>(value))), Helper> {
        auto newMap = boost::fusion::insert(data->map, boost::fusion::end(data->map), boost::fusion::make_pair<std::tuple<KeyTypes...>>(value));
        return MapInserter<ValueType, decltype(newMap), Helper>(std::move(newMap), data);
    }
    MapInserter() : data(std::make_shared<Helper>()) { }
    MapInserter(MapType&& _map, std::shared_ptr<ParentType> _parent) : data(std::make_shared<Helper>(std::move(_map), _parent)) {}
    MapInserter(MapInserter&&) = default;
    MapInserter(const MapInserter&) = default;
};
int main() {
    auto mc = MapInserter<std::string>().
        Insert<int, char, float>("***int, char, float***").
        Insert<float, double>("***float, double***").
        Insert<int>("***int***").
        Insert<unsigned, bool>("***unsigned, bool***");
    Map<decltype(mc)> map(mc);
    std::cout << map.at<int, char, float>() << std::endl; // "***int, char, float***"
    std::cout << map.at<int, char, double>() << std::endl; // "***int, char, float***"
    std::cout << map.at<char>() << std::endl; // "***int***"
    return 0;
}

【讨论】:

  • 我不得不承认我看不出这有什么用处。
【解决方案3】:
template<class...>struct types { typedef types type; };
template<class T, class types>struct type_index;
template<class T, class...Ts>
struct type_index<T,types<T, Ts...>>:
  std::integral_constant<unsigned,0>
{};
template<class T, class T0, class...Ts>
struct type_index<T,types<T0, Ts...>>:
  std::integral_constant<unsigned,type_index<T,types<Ts...>::value+1>
{};

template<template<class>class filter, class types_in, class types_out=types<>, class details=void>
struct filter;

template<template<class>class filter, class T0, class... Ts, class... Zs>
struct filter<filter, types<T0,types...>, types<Zs...>,
  typename std::enable_if< filter<T0>::value >::type
>: filter<filter, types<types...>, types<Zs...,T0>>
{};
template<template<class>class filter, class T0, class... Ts, class... Zs>
struct filter<filter, types<T0,types...>, types<Zs...>,
  typename std::enable_if< !filter<T0>::value >::type
>: filter<filter, types<types...>, types<Zs...>>
{};
template<template<class>class filter, class... Zs>
struct filter<filter, types<>, types<Zs...>,
  void
>: types<Zs...>
{};

template<typename T>
struct convertable_to_test {
  template<typename U>
  using test = std::is_convertible<U, T>;
};

template<class T, class types>
struct get_convertable_to_types:filter< convertable_to_test<T>::template test, types> {};

这是一个开始。

创建您的系统支持的所有类型的主types&lt;Ts...&gt;。打电话给SupportedTypes

将上述列表中每种类型偏移的types&lt;Ts...&gt; 映射到std::vector&lt;unsigned&gt;。现在您可以在运行时存储类型的集合。将此称为运行时类型向量。

将条目types&lt;Args...&gt; 添加到映射时,对types&lt;Args...&gt; 中的每个类型运行get_convertable_to_types,并在types&lt; types&lt;...&gt;... &gt; 中构建一个叉积。将生成的运行时类型向量的指数数量存储在您的实现详细信息图中。

当您使用types&lt;Ts...&gt; 查询时,转换为运行时类型向量,并在实现详细信息映射中查找。完成了!

另一种方法是编写get_convertable_from_types,并在查询点映射到指数数量的types&lt;Ts...&gt;,将每个转换为运行时类型向量。向地图添加内容时,仅存储一个运行时类型向量。这具有较慢的查找性能,但更快的设置性能,并且使用的内存要少得多。

我正要完成这个,但很忙。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-03
    • 1970-01-01
    • 2017-11-28
    • 2020-12-30
    • 2016-06-26
    • 2012-02-29
    • 2021-10-11
    相关资源
    最近更新 更多