Remove_if с вектором, содержащим варианты - PullRequest
0 голосов
/ 01 февраля 2019

У меня есть два разных объекта:

struct TypeA {
    std::size_t no;
    std::string data;
    std::string data2;
};

struct TypeB {
    std::size_t no;
    std::string data;
    std::string data2;
    std::string data3;
};

Они хранятся в std::vector с std::variant

std::vector<std::variant< TypeA, TypeB>> ab;

Теперь я хочу удалить все элементы, которые были членом no = 0.

Без std::variant с вектором, содержащим только TypeA Я бы сделал это так:

ab.erase(std::remove_if(ab.begin(), ab.end(),
    [](const TypeA& a) { return a.no == 0; }), ab.end());

Но как включить std::variant?Я пытался придумать что-то с std::visit, но я не могу объявить это в предикате std::remove_if или я могу?

Ответы [ 2 ]

0 голосов
/ 01 февраля 2019

Если вы хотите получить доступ к «одному и тому же» элементу данных разных типов, то эти типы должны быть подклассами общего полиморфного базового класса, определяющего этот элемент данных.

Однако в вашем случае где TypeA и TypeB не связаны, вам нужно будет сделать безопасный тип доступа к соответствующему элементу данных.Решение, предоставленное @aschepler, показывает это в общем виде, используя std::visit functor;следующее решение без std::visit (следовательно, не так элегантно, но все еще работает):

ab.erase(std::remove_if(ab.begin(), ab.end(),
    [](const std::variant< TypeA, TypeB>& v) { 
      int no;
      if (v.index()==0) {
         no = std::get<0>(v).no;
      } else {
         no = std::get<1>(v).no;
      }
      return no==0;
    }), ab.end());
0 голосов
/ 01 февраля 2019

Да, std::visit может помочь.Функтор, переданный в visit, просто должен иметь возможность принимать каждый тип variant, и самый простой способ сделать это с помощью универсальной лямбды:

ab.erase(
    std::remove_if(
        ab.begin(),
        ab.end(),
        [](const auto &v) {
            return std::visit(
                [](const auto &obj) { return obj.no == 0; },
                v);
    }),
    ab.end());

Здесь тип v для внешней лямбды всегда используется const std::variant<TypeA, TypeB>&, а auto просто удобнее, чем набирать std::variant<TypeA, TypeB>.Но для внутренней лямбды важно, чтобы лямбда была родовой, потому что visit создаст экземпляр своего шаблона operator() с обоими TypeA и TypeB.

...