Шаблонная функция C для перебора любого поля-члена коллекции

#c #templates #iterator

#c #шаблоны #Итератор

Вопрос:

Я пытаюсь написать шаблонную функцию, которая выполняет перебор по заданному пользователем полю в некоторой коллекции структур. Например, я хочу написать следующий C :

 struct Example {
    int a;
    bool b;
};

template<std::function<Fieldamp; (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 amp;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 , это также было бы приемлемо.

Ответ №1:

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

 void main() {
  std::array<Example, 2> exArray{ {5, true}, {8, false} };
  std::list<Example> exList{ exArray.begin(), exArray.end() };

  auto access_a = [](Exampleamp; e)->intamp;{ return e.a;};
  auto access_b = [](Exampleamp; e)->boolamp;{ 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. Статья 27

Чтобы не требовалось меньше шаблонного кода, рассмотрите возможность использования некоторых библиотек сериализации, которые решают проблему «перебора полей класса», например, boost serialization или magic get.

Комментарии:

1. Этот ответ вместе с указателем на параметры элемента содержит это. Вам не нужны лямбда-функции, и вы можете просто передать amp;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<amp;Example::a>(v.begin(), v.end()); // will call foo<int>(5) , foo<int>(8)
    myFunc<amp;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, amp;Example::a>(v.begin(), v.end()); // will call foo<int>(5) , foo<int>(8)
    myFunc<Example, bool, amp;Example::b>(v.begin(), v.end()); // will call foo<bool>(true) , foo<bool>(false)
}
  

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

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

Комментарии:

1. На самом деле я использую std::vector в настоящее время. Причина, по которой я не включил это в вопрос, заключается в том, что, как я предполагаю, если бы я это сделал, первый ответ, который я получил, предполагал бы, что первый аргумент myFunc всегда может быть Class* . Я хотел получить ответы более высокого качества, поэтому я выбрал тип, который требует myFunc принимать как указатели, так и итераторы.

2. Да, но итератор не является указателем. Я не хотел ответов, которые работали только для указателей.

3. К сожалению, я застрял на VS2017u2, у которого нет автоматической поддержки параметров шаблона. Однако я смог использовать объявление template<typename I, typename T, typename F> void myFunc(I begin, size_t length, F T::*field); .