Как я могу облегчить синтаксические накладные расходы на проверку значений итератора в С++?
В принципе, я немного устал писать:
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()
или я единственный, кому это раздражает?
Ответы
Ответ 1
Вы можете написать шаблон шаблона 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
Ответ 2
Хорошо, если это так много значит для вас, как насчет небольшой обертки:
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 */ }
Ответ 3
Я думаю, что это наиболее гибкое решение написать обертку, например
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;
}
он может также использоваться с другими типами контейнеров (например, вектор)
Ответ 4
Как насчет этого?
BOOST_FOREACH(auto &x : c.equal_range(...)) {
// x is a val_t &, or const if c is const.
}
Я предполагаю, что если ОК x
будет boost::optional<val_t>
, то вам действительно не нужен итератор, просто значение, поэтому ссылка в порядке.
Вы можете использовать цикл на основе С++ 11 для цикла вместо BOOST_FOREACH, если вместо std::pair
используется boost::iterator_range
(что возвращает equal_range
).
Ответ 5
Вы можете реализовать функцию-обертку, чтобы выполнить проверку != 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
Использование функции, вероятно, является более чистой реализацией, хотя и более в "С++-способе".
Ответ 6
Если вы захотите объединить свой код в анонимную функцию (похоже, вы уже используете auto
, поэтому, предполагая С++ 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;
});
}
EDIT: теперь я думаю, что это намного более чистый