C++ 在C++;要执行std::map<&燃气轮机&引用;对于元素:“容器”;使用命名变量(如键和值)而不是.first和.second进行迭代?

C++ 在C++;要执行std::map<&燃气轮机&引用;对于元素:“容器”;使用命名变量(如键和值)而不是.first和.second进行迭代?,c++,c++11,dictionary,iteration,readability,C++,C++11,Dictionary,Iteration,Readability,我不知道该找什么。 我发现了,但这不是我想做的 这是我想做的[参见下面的“无意义C++代码”)。有可能发生类似的事情吗?否则,我只需要选择“调整”迭代器作为循环中的第一行 // what I want to do: std::map<int, std::string> my_map; // ... populate my_map for(auto key, auto & value: my_map){ // do something with integer key

我不知道该找什么。 我发现了,但这不是我想做的

这是我想做的[参见下面的“无意义C++代码”)。有可能发生类似的事情吗?否则,我只需要选择“调整”迭代器作为循环中的第一行

// what I want to do:
std::map<int, std::string> my_map;
// ... populate my_map
for(auto key, auto & value: my_map){
    // do something with integer key and string value
}

下面Barry提出的一种方法是编写一个范围适配器

在没有
boost
或类似库支持的情况下执行此操作是一种痛苦,但是:

  • 编写一个范围模板。它存储2个
    类迭代器
    s,并具有
    begin()
    end()
    方法(以及您想要的任何其他方法)

  • 编写一个转换迭代器适配器。它接受一个迭代器,并将其包装起来,以便其值类型由某个函数对象F转换

  • 编写一个
    到_kv
    转换器,该转换器接受一个
    std::pair cv&
    并返回一个
    struct kv{K cv&key;V cv&value;}

  • 将3线连接成2线连接成1线,并将其称为\u kv。它获取一系列对,并返回一系列键值

  • 最终的语法是:

    std::map<int, std::string> m;
    
    for (auto kv : as_kv(m)) {
      std::cout << kv.key << "->" << kv.value << "\n";
    }
    
    这是非常小的,但有效。对于(:)循环,我通常不鼓励这种半自动的伪迭代器;使用真正的迭代器只是一个适度的额外成本,以后不会让人们感到惊讶

    (现在有了临时映射支持。还不支持平面C数组)

    范围技巧存储一个容器(可能是引用),以便将临时容器复制到(:)的
    循环期间存储的对象中。非临时容器
    容器
    类型是某种类型的
    Foo&
    ,因此它不会产生冗余副本

    另一方面,
    kv\u t
    显然只存储引用。迭代器返回临时变量可能会破坏这一
    kv_t
    实现,但我不确定如何在不牺牲更常见情况下的性能的情况下避免它


    如果您不喜欢上面的
    kv.
    部分,我们可以做一些解决方案,但它们没有那么干净

    template<class Map>
    struct for_map_t {
      Map&& loop;
      template<class F>
      void operator->*(F&& f)&&{
        for (auto&& x:loop) {
          f( decltype(x)(x).first, decltype(x)(x).second );
        }
      }
    };
    template<class Map>
    for_map_t<Map> map_for( Map&& map ) { return {std::forward<Map>(map)}; }
    

    对上述
    ->*
    憎恶的评论:

    因此,
    ->*
    被用作Haskell的
    操作符bind
    (与隐式元组解包一起),我们给它一个lambda,它获取映射中包含的数据并返回void。(Haskell式)返回类型成为void(nothing)上的映射,我将其省略为void

    这项技术有一个问题:你输了
    break
    继续哪个烂

    一个不太受哈奇·哈斯克尔启发的变体会期望lambda返回类似于
    void | std::experimental::expected
    ,如果
    T
    void
    则不返回任何内容,如果
    T
    是元组类型则返回映射,如果
    T
    是映射,则加入返回的映射类型。它还可以根据lambda想要什么(SFINAE风格的检测)来解包或不解包包含的元组


    但这对于一个如此的答案来说有点过分;这一离题指出,上述编程风格并不是一条完全的死胡同。然而,C++中的非常规性。

    可以编写类模板:

    template <class K, class T>
    struct MapElem {
        K const& key;
        T& value;
    
        MapElem(std::pair<K const, T>& pair)
            : key(pair.first)
            , value(pair.second)
        { }
    };
    
    如果
    my_map
    也是
    const
    的话,这就行不通了。您必须执行以下操作:

    template <class K, class T>
    struct MapElem {
        K const& key;
        T& value;
    
        MapElem(std::pair<K const, T>& pair)
            : key(pair.first)
            , value(pair.second)
        { }
    
        MapElem(const std::pair<K const, std::remove_const_t<T>>& pair)
            : key(pair.first)
            , value(pair.second)
        { }
    };
    
    for ( MapElem<int, const std::string> kv : my_map ){
        std::cout << kv.key << " --> " << kv.value;
    }
    

    最接近它使用的东西
    std::tie

    std::map<int, std::string> my_map;
    int key;
    std::string value;
    for(auto&& p: my_map)
    {
        std::tie(key, value) = p;
        std::cout << key << ": " << value << std::endl;
    }
    

    为了提供另一种几乎可以实现您想要的功能的方法,我在前一段时间编写了这篇文章,以避免代码中出现
    。首先
    。其次

    auto pair2params = [](auto&& f)
    {
        return [f](auto&& p) {
            f(p.first, p.second);
        };
    };
    
    现在您可以编写如下内容(假设每个
    都有一个基于范围的
    ):

    intmain()
    {
    自动值=映射{
    {0,“你好”},
    {1,“世界!”}
    };
    for_each(值、pair2参数([])(int键、常量字符串和值){
    
    cout我通常更喜欢亲吻的方式:

    template<typename KeyValuePair>
    typename KeyValuePair::first_type& key(KeyValuePair& kvp)
    {
        return kvp.first;
    }
    
    template<typename KeyValuePair>
    const typename KeyValuePair::first_type& key(const KeyValuePair& kvp)
    {
        return kvp.first;
    }
    
    template<typename KeyValuePair>
    void key(const KeyValuePair&& kvp) = delete;
    
    template<typename KeyValuePair>
    typename KeyValuePair::second_type& value(KeyValuePair& kvp)
    {
        return kvp.second;
    }
    
    template<typename KeyValuePair>
    const typename KeyValuePair::second_type& value(const KeyValuePair& kvp)
    {
        return kvp.second;
    }
    
    template<typename KeyValuePair>
    void value(const KeyValuePair&& kvp) = delete;
    
    模板
    typename KeyValuePair::第一种类型和密钥(KeyValuePair和kvp)
    {
    首先返回kvp;
    }
    模板
    const typename KeyValuePair::first_type&key(const KeyValuePair&kvp)
    {
    首先返回kvp;
    }
    模板
    无效密钥(const KeyValuePair&&kvp)=删除;
    模板
    typename KeyValuePair::第二种类型和值(KeyValuePair和kvp)
    {
    返回kvp.second;
    }
    模板
    const typename KeyValuePair::第二种类型和值(const KeyValuePair&kvp)
    {
    返回kvp.second;
    }
    模板
    无效值(常量KeyValuePair&&kvp)=删除;
    
    使用如下示例:

    for( auto&& [key, value] : container )
    
    for(auto& kvp : my_map) {
        std::cout << key(kvp) << " " << value(kvp) << "\n";
    }
    
    foreachpair (const Key& key, const Value& value, elems) {
      /* ... */
    }
    
    #define FOREACH_PREFIX   BOOST_PP_CAT(foreach_, __LINE__)
    
    #define FOREACH_BODY     BOOST_PP_CAT(FOREACH_PREFIX, _body__)
    #define FOREACH_BREAK    BOOST_PP_CAT(FOREACH_PREFIX, _break__)
    #define FOREACH_CONTINUE BOOST_PP_CAT(FOREACH_PREFIX, _continue__)
    #define FOREACH_ELEM     BOOST_PP_CAT(FOREACH_PREFIX, _elem__)
    #define FOREACH_ONCE     BOOST_PP_CAT(FOREACH_PREFIX, _once__)
    
    for(自动和kvp:my_映射){
    std::cout在中,我们使用一个名为
    foreachpair
    的宏,它可以这样使用:

    for( auto&& [key, value] : container )
    
    for(auto& kvp : my_map) {
        std::cout << key(kvp) << " " << value(kvp) << "\n";
    }
    
    foreachpair (const Key& key, const Value& value, elems) {
      /* ... */
    }
    
    #define FOREACH_PREFIX   BOOST_PP_CAT(foreach_, __LINE__)
    
    #define FOREACH_BODY     BOOST_PP_CAT(FOREACH_PREFIX, _body__)
    #define FOREACH_BREAK    BOOST_PP_CAT(FOREACH_PREFIX, _break__)
    #define FOREACH_CONTINUE BOOST_PP_CAT(FOREACH_PREFIX, _continue__)
    #define FOREACH_ELEM     BOOST_PP_CAT(FOREACH_PREFIX, _elem__)
    #define FOREACH_ONCE     BOOST_PP_CAT(FOREACH_PREFIX, _once__)
    
    当然,您可以将
    替换为
    自动
    ,以及您想在其中使用的任何限定符。它还支持
    中断
    继续

    我的最新实现如下所示:

    for( auto&& [key, value] : container )
    
    for(auto& kvp : my_map) {
        std::cout << key(kvp) << " " << value(kvp) << "\n";
    }
    
    foreachpair (const Key& key, const Value& value, elems) {
      /* ... */
    }
    
    #define FOREACH_PREFIX   BOOST_PP_CAT(foreach_, __LINE__)
    
    #define FOREACH_BODY     BOOST_PP_CAT(FOREACH_PREFIX, _body__)
    #define FOREACH_BREAK    BOOST_PP_CAT(FOREACH_PREFIX, _break__)
    #define FOREACH_CONTINUE BOOST_PP_CAT(FOREACH_PREFIX, _continue__)
    #define FOREACH_ELEM     BOOST_PP_CAT(FOREACH_PREFIX, _elem__)
    #define FOREACH_ONCE     BOOST_PP_CAT(FOREACH_PREFIX, _once__)
    
    上面的宏通过包含
    \uuuu行
    编号,为
    foreachpair
    宏中使用的各种组件提供了唯一的名称

     1 #define foreachpair(KEY, VALUE, ELEMS)                                     \
     2   for (auto&& FOREACH_ELEM : ELEMS)                                        \
     3     if (false) FOREACH_BREAK: break; /* set up the break path */           \
     4     else if (bool FOREACH_CONTINUE = false) {} /* var decl */              \
     5     else if (true) goto FOREACH_BODY; /* skip the loop exit checks */      \
     6     else for (;;) /* determine whether we should break or continue. */     \
     7       if (!FOREACH_CONTINUE) goto FOREACH_BREAK; /* break */               \
     8       else if (true) break; /* continue */                                 \
     9       else                                                                 \
    10         FOREACH_BODY:                                                      \
    11         if (bool FOREACH_ONCE = false) {} /* var decl */                   \
    12         else for (KEY = std::get<0>(                                       \
    13                       std::forward<decltype(FOREACH_ELEM)>(FOREACH_ELEM)); \
    14                   !FOREACH_ONCE; FOREACH_ONCE = true)                      \
    15           for (VALUE = std::get<1>(                                        \
    16                    std::forward<decltype(FOREACH_ELEM)>(FOREACH_ELEM));    \
    17                !FOREACH_CONTINUE; FOREACH_CONTINUE = true)
    
    1#定义foreachpair(键、值、元素)\
    2个用于(自动和前置要素:要素)\
    3 if(false)FOREACH_BREAK:BREAK;/*设置中断路径*/\
    4如果(bool FOREACH_CONTINUE=false){}/*var decl*/\
    5否则,如果(true)转到FOREACH_BODY;/*跳过循环退出检查*/\
    6对于(;)/*确定我们应该中断还是继续。*/\
    7如果(!FOREACH\u CONTINUE)转到FOREACH\u BREAK;/*BREAK*/\
    8如果为(真)则中断;/*继续*/\
    9其他
    
     1 #define foreachpair(KEY, VALUE, ELEMS)                                     \
     2   for (auto&& FOREACH_ELEM : ELEMS)                                        \
     3     if (false) FOREACH_BREAK: break; /* set up the break path */           \
     4     else if (bool FOREACH_CONTINUE = false) {} /* var decl */              \
     5     else if (true) goto FOREACH_BODY; /* skip the loop exit checks */      \
     6     else for (;;) /* determine whether we should break or continue. */     \
     7       if (!FOREACH_CONTINUE) goto FOREACH_BREAK; /* break */               \
     8       else if (true) break; /* continue */                                 \
     9       else                                                                 \
    10         FOREACH_BODY:                                                      \
    11         if (bool FOREACH_ONCE = false) {} /* var decl */                   \
    12         else for (KEY = std::get<0>(                                       \
    13                       std::forward<decltype(FOREACH_ELEM)>(FOREACH_ELEM)); \
    14                   !FOREACH_ONCE; FOREACH_ONCE = true)                      \
    15           for (VALUE = std::get<1>(                                        \
    16                    std::forward<decltype(FOREACH_ELEM)>(FOREACH_ELEM));    \
    17                !FOREACH_CONTINUE; FOREACH_CONTINUE = true)
    
    #include <map>
    #include <string>
    #include <iostream>
    
    using namespace std;
    
    int main() {
        map<int, string> my_map;
    
        my_map[0] = "hello";
        my_map[1] = "world";
    
        for (auto&& [key, value] : my_map) {
            cout << key << "," << value << "\n";
        }
    
        return 0;
    }
    
    $ clang++ -std=c++17 test.cpp -o program
    
    $ ./program
    0,hello
    1,world