2014-09-30 4 views
20

Я хотел бы скопировать до N элементов.std :: copy n элементов или до конца

template< class InputIt, class Size, class OutputIt> 
OutputIt myCopy_n(InputIt first, InputIt last, Size count, OutputIt result) 
{ 
    Size c = count; 
    while (first != last && c > 0) { 
     *result++ = *first++; 
     --c; 
    } 
    return result; 
} 

Есть ли способ сделать это с помощью функций std? Я мог бы также:

template< class InputIt, class Size, class OutputIt> 
OutputIt myCopy_n(InputIt first, InputIt last, Size count, OutputIt result) 
{ 
    if(std::distance(first, last) > count) 
     return std::copy_n(first,count,result); 
    return std::copy(first,last,result); 
} 

Однако, помимо того, что громоздкий, он идет в диапазоне дважды (расстояние, копия). Если я использую итератор преобразования или фильтр-итератор, это O (N) ненужные вызовы моей функции filter/transform.

template <class InputIt, class OutputIt> 
OutputIt copy_n_max(InputIt begin, InputIt end, OutputIt last, size_t count) 
{ 
    return std::copy_if(begin, end, last, 
         [&count](typename std::iterator_traits<InputIt>::reference) 
         { return count--> 0; }); 
} 

int main() 
{ 
    std::vector<int> v({1,2,3,4,5,6,7,8,9}), out; 
    copy_n_max(v.begin(), v.end(), std::back_inserter(out), 40); 
    for(int i : out) std::cout <<i << " ,"; 
} 

выходы 1,2,3,4,5,6,7,8,9,

однако, это будет продолжаться до конца, а не число раз. так еще более ненужные звонки на мой фильтровальной функции/преобразования ...

+1

Можете ли вы уточнить, что вы пытаетесь сделать? – 2014-09-30 11:09:08

+6

@remyabel Кажется довольно специфичным для меня. –

+1

Честно говоря, ваше первое решение кажется самым простым; назовите это что-то вроде copy_upto_n() и назовите это днем; Он пересекает диапазон только один раз (наименьшее количество ограничений), и он никогда не делает больше, чем подсчет различий ввода, в отличие от третьего решения. Я был действительно удивлен тем, что copy_n не ведет себя таким образом, в первую очередь ... –

ответ

14

Если у вас есть доступ ко всей структуре данных, и, следовательно, его размер, вы можете использовать следующее:

std::vector<int> v1, v2; 
std::copy_n(v2.begin(), std::min(NUM, v2.size()), std::back_inserter(v1)); 

Если у вас есть доступ к только итераторам, я не знаю, как это сделать, используя только std-функции без вычисления расстояния. Это дешево для итераторов с произвольным доступом, но дубликаты работают для других типов.

std::vector<int>::iterator i_begin, i_end, o_begin; 
std::copy_n(i_begin, std::min(NUM, std::distance(i_begin, i_end)), o_begin); 
+0

Да, ваш путь менее громоздкий, но время работы такое же. Я думаю, что это лучшее, что мы получим. придется копировать самостоятельно или оплачивать цену за «расстояние», если функция фильтра/преобразования пренебрежимо мала. Кажется, стыдно, что copy_n он защищен конечным итератором. – user2232888

+1

@ user2232888 Сложность 'std :: distance' является постоянной для итераторов с произвольным доступом.Таким образом, либо 'std :: distance' является бесплатным, либо вы знаете размер контейнера перед началом работы, и первое решение Neil Kirk (http://stackoverflow.com/users/2068573/neil-kirk) будет работать. –

+1

Johathan Mee, это неправильно, когда все, что у вас есть, - итераторы. Вызов ++ может быть дорогостоящим, если вы используете итераторы фильтра/преобразования ... – user2232888

8

Я хотел бы пойти на что-то вроде этого:

template <class InputIt, class OutputIt> 
OutputIt copy_n_max(InputIt begin, InputIt end, OutputIt last, size_t count) 
{ 
    return std::copy_if(begin, 
         end, 
         last, 
         [&count](typename std::iterator_traits<InputIt>::reference) -> bool 
         { 
          if (count > 0) 
          { 
           --count; 
           return true; 
          } 
          return false; 
         }); 
} 

Используя copy_if предикат, чтобы проверить, был ли достаточно этого входа копируется. Главное преимущество, которое я вижу здесь, не является дополнительным std::distance.

Live example on IDEOne

+0

Я уверен, что компилятор упростит его. И я считаю это более читаемым. – Johan

+0

похоже работа. Мне нравится это решение – user2232888

+1

'decltype (* begin) &' => нет, не совсем. То, что вы хотите, это 'typename std :: iterator_traits :: reference', чтобы он работал с * прокси * (например, в' std :: vector '). В C++ 14 вы также можете использовать 'auto &', а не оглядываться назад. –

7

Там простой способ использовать std::copy_if -overload добавленных C++ 11 для задачи (только нужно InputIterators):

template< class InputIt, class Size, class OutputIt> 
OutputIt myCopy_n(InputIt first, InputIt last, Size count, OutputIt result) 
{ 
    return std::copy_if(first, last, result, 
     [&](typename std::iterator_traits<InputIt>::reference) 
     {return count && count--;}); 
} 

BTW: Это становится еще лучше в C + +14, нет необходимости в переменном или таком сложный аргумент:

std::copy_if(first, last, result, 
    [count = some_complicated_expression](auto&&) mutable 
    {return count && count--;}); 
+0

Единственное раздражение - ненужные проверки, когда' count' больше, чем размер диапазона. – jrok

+0

Ну, да , можно добавить некоторый шаблон со всеми оптимизациями и ярлыками, которые используются в стандартной библиотеке. Я думаю, что перегрузка для 'std :: copy_n' должна быть работоспособной. – Deduplicator

+0

@Deduplicator: Ах, я полностью замалчивал этот факт! Действительно, это 'copy_if', а не' copy_until'. –

1

Вы можете использовать copy_if с пользовательским predicator, и он работает на старых ве rsions C++.

#include <algorithm> 
#include <iostream> 
#include <vector> 
#include <iterator> 


struct LimitTo 
{ 
    LimitTo(const int n_) : n(n_) 
    {} 

    template< typename T > 
    bool operator()(const T&) 
    { 
     return n-->0; 
    } 

    int n; 
}; 

int main() 
{ 
    std::vector<int> v1{ 1,2,3,4,5,6,7,8 }; 
    std::vector<int> v2; 

    std::copy_if(std::begin(v1), std::end(v1), std::back_inserter(v2), LimitTo(3)); 

    std::copy(std::begin(v1), std::end(v1), std::ostream_iterator<int>(std::cout,", ")); 
    std::cout << std::endl; 
    std::copy(std::begin(v2), std::end(v2), std::ostream_iterator<int>(std::cout,", ")); 
    std::cout << std::endl; 
} 

Этот пример копирует n элементов, используя предикат LimitTo.

Смежные вопросы