如何实现地图和集合通用的模板?

Posted

技术标签:

【中文标题】如何实现地图和集合通用的模板?【英文标题】:How to implement a template common for maps and sets? 【发布时间】:2020-12-22 19:29:47 【问题描述】:

我正在从数据库中加载各种数据,并且已经实现了一个模板方法:

template <typename R>
bool
loadMap(map<const string, R> &store, const char *query) 

...
      for (auto row : rows(query)) 
          store.insert(make_pair(row[0], R(row[1], row[2], ....));
      

该方法将query 发送到数据库服务器并使用结果填充store。第一列成为映射的键,其余字段形成值结构。这行得通。

但是,在某些情况下,查询仅返回 一个 列——存储在 set(或 unordered_set)中——没有任何关联的值。

目前这些情况是由它们自己的方法处理的,我想将我的 loadMap-function 扩展到这些其他类型的容器——也许,将其重命名为 loadContainer

这个新的loadContainer 的声明看起来如何,以及它如何仅使用键调用store.insert(),没有值?

(我知道将集合转换为所有值都为空的地图的躲闪,但我想避免这种情况。)

实际上,我什至想不出处理mapunordered_map 的方法——虽然insert 完全一样,但我该如何声明这样的函数?这两种地图类型似乎没有共同的祖先(也没有“接口”,使用 Java 术语)...

【问题讨论】:

你可以使用map&lt;const string, std::optional&lt;R&gt;&gt; 我正要发表评论,但被打败了。使用std::optional&lt;R&gt; 谢谢你们,但它仍然是map,不是吗?不是set——甚至不是unordered_map... 在我的工作中(使用 Java),我们遇到了这个问题,最终得到了一个核心 template&lt;Container&gt; void queryAll(Container&lt;T&gt;&amp;) 来完成真正的工作,然后是三个辅助包装器:List&lt;T&gt; queryAll()T queryOneOrThrow()Optional&lt;T&gt; queryOneOrNone() 重载。 @MooingDuck,Java 有运行时类型信息 (RTTI)!您可以在运行时动态创建新的,然后根据返回的列的名称/类型为每个 SQL 查询创建这些新创建类型的对象。但是,在 C++ 中没有这种(昂贵的)奢侈品…… 【参考方案1】:

你可能有重载

template <typename R>
bool loadContainer(std::map<string, R> &store, const char *query) 

    // ...
    for (auto row : rows(query)) 
        store.insert(std::make_pair(row[0], R(row[1], row[2] /*, ...*/));
    
    // ...


bool loadContainer(std::set<string> &store, const char *query) 

    // ...
    for (auto row : rows(query)) 
        store.insert(row[0]);
    
    // ...

或者,为了避免过载,您可以这样做 (C++17)

template <typename Container>
bool loadContainer(Container& store, const char *query) 

    // ...
    for (auto row : rows(query)) 
        if constexpr (is_map<Container>::value) 
            using R = typename Container::mapped_type;
            store.insert(std::make_pair(row[0], R(row[1], row[2] /*, ...*/));
         else 
            store.insert(row[0]);
        
    
    // ...

具有适当的特征,例如

template <typename T, typename Enabler = void>
struct is_map : std::false_type ;

template <typename T>
struct is_map<T, std::void_t<typename T::mapped_type>> : std::true_type ;

【讨论】:

我希望 rows() 调用真的很简单 :) 但是,也许,我应该把它变成一个自己的迭代器...... Khm......不管怎样,@ 987654326@ 与 unordered_map?这两种类型是否真的也需要单独的方法——即使代码相同 您可能仍然使用 SFINAE 或 c++20 概念。 不,它必须使用 g++-4.4 与 -std=gnu++0x 一起工作 SFINAE 甚至可以在 C++11 之前使用,但我们通常必须重写 C++11 后 std 中提供的一些实用程序。 @MikhailT。 不,它必须使用 g++-4.4 与 -std=gnu++0x 一起使用 -- boost 可能是你的朋友。【参考方案2】:

解决了distinguishing between maps and sets 的问题后,我能够为不同的容器类型实现不同的insert 函数。

我的loadMap 模板化函数只是调用insert(store, key, value) -- 并且在编译时根据store 的类型选择正确的insert 实现。 (对于集合,value 将被忽略。)

【讨论】:

以上是关于如何实现地图和集合通用的模板?的主要内容,如果未能解决你的问题,请参考以下文章

如何在模板中显示导航页面的路径结构(站点地图)

斯卡拉:如何合并的地图集合

报表中如何实现中国地图钻取到各省地图

微信小程序如何设置地图导航

模板模式

百度地图API 如何根据现有坐标集合生成曲线轨迹?