Ответ 1
Второй - самый идиоматический.
- Первый не работает с прокси (std::vector <bool> )
- Третий не работает с указателями.
При написании общих функций для диапазонов "итератор" я обычно делаю:
template <typename Iter> auto func(Iter &first, Iter &last)
{
using IterType = typename std::decay<decltype(*first)>::type;
...
}
Другой способ:
template <typename Iter> auto func(Iter &first, Iter &last)
{
using IterType = typename std::iterator_traits<Iter>::value_type;
...
}
И еще третья:
template <typename Iter> auto func(Iter &first, Iter &last)
{
using IterType = typename Iter::value_type;
...
}
Без применения iterator_traits
.
В теории мои функции должны принимать только итераторы как first
и last
, а вторая форма идеально (imho) - самый идиоматический способ получить тип. Но использует typename std::decay<decltype(*first)>::type
самую общую идиому, чтобы не налагать ограничений на Iter
, как имеющие value_type
, определенные?
Второй - самый идиоматический.
Ни один из них не является вполне идиоматическим; вы должны передавать итераторы по значению, а не по ссылке. Здесь подпись для for_each в gcc 4.9:
template<typename _InputIterator, typename _Function>
_Function
for_each(_InputIterator __first, _InputIterator __last, _Function __f)
Как вы можете видеть, он прошел по значению. Ваши функции не будут работать в идиоматическом использовании:
func(v.begin(), v.end()); // error, binding non-const ref to rvalue!
Кроме того, переход через iterator_traits больше, чем просто идиоматический, это в основном требуется. Что касается STL, то такие typedef определяются только через iterator_traits: http://en.cppreference.com/w/cpp/concept/ForwardIterator. iterator_traits предоставляет разумные значения по умолчанию для общего случая, но может быть специализированным (как и для указателей) делать разные вещи. Не проходя через iterator_traits в основном означает, что кто-то может написать совместимый итератор, который работал с STL, но не с вашим кодом.