Как я могу облегчить синтаксические накладные расходы при проверке значений итераторов в C ++? - PullRequest
14 голосов
/ 24 октября 2011

В принципе, я немного устал писать:

std::map<key_t, val_t> the_map;
...
auto iterator = the_map.find(...);
if(iterator != the_map.end()) { // note the "inversed" logic and logically superflous end() call
  ...
}

Что действительно имеет смысл:

if(auto x=the_map.find(...)) {
  ... // x could either be an iterator or maybe something like boost::optional<val_t>
}

Есть ли какой-то уровень техники, который определяет некоторые вспомогательные средства для сокращения синтаксиса != container.end() или меня это раздражает только один?

Ответы [ 6 ]

7 голосов
/ 24 октября 2011

Ну, если это так много значит для вас, как насчет маленькой обертки:

template <typename Container>
inline bool find_element(Container const & c,
                         typename Container::const_iterator & it,
                         typename Container::value_type const & val)
{
  return (it = c.find(val)) == c.end();
}

Использование:

std::vector<int> v;
std::vector<int>::const_iterator it;

if (find_element(v, it, 12)) { /* use it */ }
7 голосов
/ 24 октября 2011

Вы можете написать шаблон класса auto_iterator_impl и использовать его через шаблон функции auto_iterator, который возвращает экземпляр auto_iterator_impl, который можно неявно преобразовать в true или false:

Рабочая реализация с минимальными функциональными возможностями и учетом:

template<typename C>
struct auto_iterator_impl
{
    C & c;
    typename C::iterator  it;
    auto_iterator_impl(C & c,  typename C::iterator & it) : c(c), it(it) {}
    operator bool() const { return it != c.end(); }
    typename C::iterator operator->() { return it; }
};

template<typename C>
auto_iterator_impl<C> auto_iterator(C & c, typename C::iterator  it)
{
    return auto_iterator_impl<C>(c, it);
}

Тестовый код:

void test(std::map<int, int> & m, int key)
{
    if (auto x = auto_iterator(m, m.find(key)))
    {
        std::cout << "found = " << x->second << std::endl;
        x->second *= 100; //change it
    }
    else
        std::cout << "not found" << std::endl;
}

int main()
{
    std::map<int,int> m;
    m[1] = 10;
    m[2] = 20;  
    test(m, 1);
    test(m, 3);
    test(m, 2);

    std::cout <<"print modified values.." <<std::endl;
    std::cout << m[1] << std::endl;
    std::cout << m[2] << std::endl;
}

Вывод:

found = 10
not found
found = 20
print modified values..
1000
2000

Демонстрационная версия онлайн: http://www.ideone.com/MnISh

1 голос
/ 24 октября 2011

Как насчет этого?

BOOST_FOREACH(auto &x : c.equal_range(...)) {
    // x is a val_t &, or const if c is const.
}

Я предполагаю, что если все нормально, если x равно boost::optional<val_t>, то вам на самом деле не нужен итератор, просто значение, так чтоссылка в порядке.

Вы можете использовать цикл for на основе диапазона C ++ 11 вместо BOOST_FOREACH, если вы используете boost::iterator_range вместо std::pair (что и возвращает equal_range).

1 голос
/ 24 октября 2011

Я думаю, что это наиболее гибкое решение для написания оболочки вроде

template<class Iterable>
bool CheckIterator(const typename Iterable::iterator& iter, const Iterable& collection)
{
    return !(iter == collection.end());
}

и используйте его

    map<int,string> m;
    m[1]="hello";
    m[2]="world";
    map<int,string>::iterator it = m.find(2);
    if(CheckIterator(it,m))
    {
        cout<<it->second;
    } 

может использоваться и с другими типами контейнеров (например, векторными)

0 голосов
/ 24 октября 2011

Если вы готовы объединить свой код с анонимной функцией (похоже, вы уже используете auto, поэтому предположим, что c ++ 11)?

#include <iostream>
#include <map>

template <typename Container, typename ExecF>
void exec_if(Container const& cont, typename Container::const_iterator it, ExecF f)
{
  if (it != cont.end())
    f();
}

int main(void)
{
  std::map<int, int> bar;
  bar.insert(std::make_pair(1, 1));
  bar.insert(std::make_pair(2, 2));
  bar.insert(std::make_pair(3, 3));

  auto it = bar.find(2);
  exec_if(bar, it, [&it]()
    { 
      std::cout << it->first << ' ' << it->second << std::endl; 
    });

}

РЕДАКТИРОВАТЬ: теперь я думаю, что этонамного чище

0 голосов
/ 24 октября 2011

Вы можете реализовать функцию-обертку для проверки != map.end() или использовать препроцессор, используя макрос, подобный следующему:

#define map_find_if(iterator, container, func) \
    auto (iterator) = (func); \
    if ((iterator) != (container).end())

Используйте это так:

map_find_if(iter, map, map.find(key)) {
   // found the key
}
// else clause could go here

Использование функции, вероятно, является более чистой реализацией, и более подробно "C ++ way".

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...