Функция шаблона C ++ для перебора любого поля члена коллекции - PullRequest
2 голосов
/ 07 марта 2019

Я пытаюсь написать шаблонную функцию, которая перебирает указанное пользователем поле в некоторой коллекции структур. Например, я хочу написать следующий 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, это также будет приемлемо.

Ответы [ 2 ]

3 голосов
/ 07 марта 2019

Обычно я использую что-то вроде:

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

Чтобы потребовать меньшего количества стандартного кода, рассмотрите возможность использования некоторых библиотек сериализации, которые решают эту проблему "перебора полей классов", например повышение сериализации или Волшебное получение .

2 голосов
/ 07 марта 2019

Это просто, если вы знаете указатель на синтаксис члена и т.п. К сожалению, так редко используется, это своего рода эзотерическая особенность языка:

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 <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)
}

Немного ОТ на ваш вопрос, но я не понимаю, почему вы усложняете себя с этой инициализацией std::list. В C ++ ваш первый контейнер выбора должен быть std::vector.

Также нет std::iterator

...