【问题标题】:C++ Template Function to Iterate Over any Collection Member Field用于迭代任何集合成员字段的 C++ 模板函数
【发布时间】:2019-03-06 21:01:19
【问题描述】:

我正在尝试编写一个模板函数,该函数在某些结构集合中迭代用户指定的字段。比如我想写如下C++:

struct Example {
    int a;
    bool b;
};

template<std::function<Field& (Class)> GetField, typename Field, typename Class>
void myFunc(std::iterator<Class> begin, size_t const length) {
    cout << length << endl;
    for (size_t i{ 0 }; i < length; ++begin, ++i) {
        Field const &field{ GetField(*begin) };
        // Forward field to some other template function
        anotherTemplateFunction<Field>(field);
    }
}

void main() {
    Example exArray[]{ {5, true}, {8, false} };
    std::list<Example> exList{ exArray, exArray + _countof(exArray) }

    // Examples of how I would like to call myFunc...
    myFunc<Example::a>(exArray, _countof(exArray));
    myFunc<Example::b>(exList.begin(), exList.size());
}

以上内容不起作用,但希望意图很明确。如何编写 myFunc 模板方法来完成对每个迭代项的某些字段的通用迭代?或者,如果有某种方式(在 Boost 或标准库中)可以直接在 exArray[i].a 上创建迭代器,那也是可以接受的。

【问题讨论】:

    标签: c++ templates iterator


    【解决方案1】:

    我通常使用的是这样的:

    void main() {
      std::array<Example, 2> exArray{ {5, true}, {8, false} };
      std::list<Example> exList{ exArray.begin(), exArray.end() };
    
      auto access_a = [](Example& e)->int&{ return e.a;};
      auto access_b = [](Example& e)->bool&{ return e.b;};
      myFunc(exArray.begin(), exArray.end(), access_a);
      myFunc(exList.begin(), exList.end(), access_b);
    }
    
    template<class ForwardIt, class Accessor>
    void myFunc(ForwardIt begin,ForwardIt end, Accessor accessor) {
        cout << end - begin << endl;
        for (auto it = begin; it != end; it++) {
          // Forward field to some other template function
          anotherTemplateFunction(accessor(*it));
        }
    }
    

    请注意我是如何使用 std::array 而不是原始 c 样式数组的。 如果您可以访问 c++11 编译器,则应始终首选 std::array(或 std::vector)而不是原始 c 数组。 ES.27

    为了减少样板代码,考虑使用一些序列化库来解决这个“迭代类字段”问题,例如boost serializationmagic get

    【讨论】:

    • 这个答案以及指向成员参数的指针都有它。您不需要 lambda 函数,只需将 &Example::a 作为第二个参数直接传递即可。
    【解决方案2】:

    如果您知道指向成员语法的指针等,这很简单。不幸的是,它很少使用,是该语言的一种深奥特征:

    template <class T> void foo(T);
    
    template <auto Field, class It>
    auto myFunc(It begin, It end)
    {
        for (; begin != end; ++begin)
        {
            foo((*begin).*Field);
        }
    }
    
    int main()
    {
        std::vector<Example> v{{5, true}, {8, false}};
    
        myFunc<&Example::a>(v.begin(), v.end()); // will call foo<int>(5) , foo<int>(8)
        myFunc<&Example::b>(v.begin(), v.end()); // will call foo<bool>(true) , foo<bool>(false)
    }
    

    对于template &lt;auto Field,您需要 C++17。

    对于 C++11,语法更冗长:

    template <class T, class F, F T::* Field, class It>
    void myFunc(It begin, It end)
    { /* same */ }
    
    int main()
    {
        std::vector<Example> v{{5, true}, {8, false}};
    
        myFunc<Example, int, &Example::a>(v.begin(), v.end()); // will call foo<int>(5) , foo<int>(8)
        myFunc<Example, bool, &Example::b>(v.begin(), v.end()); // will call foo<bool>(true) , foo<bool>(false)
    }
    

    对您的问题有点 OT,但我不明白您为什么将 std::list 的初始化复杂化。在 C++ 中,您选择的第一个容器应该是 std::vector

    也没有std::iterator

    【讨论】:

    • 我目前实际上正在使用std::vector。我没有把它放在问题中的原因是因为我猜如果我这样做了,我得到的第一个答案会假设myFunc 的第一个参数可能总是Class*。我想要更高质量的答案,所以我选择了需要myFunc 来接受指针和迭代器的类型。
    • 是的,但迭代器不是指针。我不想要对指针有用的答案。
    • 不幸的是,我卡在 VS2017u2 上,它没有自动模板参数支持。但是,我能够使用声明 template&lt;typename I, typename T, typename F&gt; void myFunc(I begin, size_t length, F T::*field);
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多