2014-02-21 1 views
4

Мой вопрос прост, смотри пример:создать контейнер из другого контейнера, применяя каждый элемент некоторой функции в C++

std::array<int,6> a = {{0,1,2,3,4,5}}; // -- given container. 
auto F = [](int i) { return i*i; }; // -- given function. 

std::vector<int> v; // need create 

// my solution: 
v.reserve(a.size()); 
for(std::size_t i = 0; i < a.size(); ++i) 
    v.push_back(F(a[i])); 



// but I need something like 
    std::vector<int>v(a.begin(), a.end(), <|applying each element to F|>); 

Могу ли я создать что-то вроде контейнера выше, не вызывая резерв явно и любое перераспределение?

EDIT:

  1. Я хочу, чтобы избежать резерва; потому что othercase мое первое решение хорошее для меня :)
  2. Я хочу избежать изменения размера; потому что он инициализировал каждый элемент по умолчанию ctor.
  3. Я буду использовать это в реальном проекте, который может включать в себя множество 3-х партийных библиотек (boost, Soft-STL, ...).
+6

Будет 'станд :: transform' не делать это? –

+0

['Инициаторы преобразования] (http://www.boost.org/doc/libs/1_55_0/libs/iterator/doc/transform_iterator.html) могут быть полезны здесь. – Borgleader

+0

Я хочу также избегать v.reserve (a.size()). – Khurshid

ответ

2

Другим решением является использование boost::transform_iterator. Преимущество состоит в том, что вы можете передавать итераторы конструктору контейнера. Это позволяет избежать перераспределения памяти по сравнению с использованием std::back_inserter или для вызова reserve или resize по месту назначения. Все в одном заявлении:

std::vector<int> result(
     boost::make_transform_iterator(std::begin(a), F) 
    , boost::make_transform_iterator(std::end(a), F) 
    ); 

Вы можете достичь terser синтаксиса, хотя, как это:

std::vector<int> result(transform_range(a, F)); 

transform_range реализации:

template<class Iterator> 
struct AutoSequence 
{ 
    Iterator const beg_, end_; 

    template<class T> 
    operator std::vector<T>() const { 
     return {beg_, end_}; 
    } 
}; 

template<class Function, class InSeq> 
auto transform_range(InSeq const& in) -> AutoSequence<decltype(boost::make_transform_iterator<Function>(in.begin()))> { 
    return { 
      boost::make_transform_iterator<Function>(std::begin(in)) 
     , boost::make_transform_iterator<Function>(std::end(in)) 
     }; 
} 

template<class Function, class InSeq> 
auto transform_range(InSeq const& in, Function&& f) -> AutoSequence<decltype(boost::make_transform_iterator(in.begin(), f))> { 
    return { 
      boost::make_transform_iterator(std::begin(in), f) 
     , boost::make_transform_iterator(std::end(in), f) 
     }; 
} 
+2

Полностью ненужное использование сторонней библиотеки. Что это дает вам? Одна строка кода сохранена здесь, тысяча добавлена ​​в заголовок. Потрясающие. –

+0

@LightnessRacesinOrbit Плачь мне реку. –

+0

Профессиональный контраргумент. –

5

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

#include <algorithm> // std::transform 
#include <iterator> // std::back_inserter 

.... 

transform(a.begin(), a.end(), back_inserter(v), F); 

Вы можете позвонить v.reserve(asize()) первым, чтобы избежать повторного распределения.

+1

Коварное использование ADL. –

+2

@ KerrekSB Я чувствовал себя особенно ленивым. Пятница - новое воскресенье. – juanchopanza

+2

@KerrekSB Что такое ADL? : P – herohuyongtao

9

Стандартный алгоритм std::transformровно это!

std::vector<int> v(a.size()); 
std::transform(
    std::begin(a), std::end(a), 
    std::begin(v), 
    F 
); 

Вы можете начать с пустым вектором и использовать std::back_inserter, если вам нравится:

std::vector<int> v; 
std::transform(
    std::begin(a), std::end(a), 
    std::back_inserter(v), 
    F 
); 

Но вы подвергая себя ненужное перераспределение, если вы сделаете это (если вы reserve первыми, как в вашей первоначальной попытке). Вы можете сами решить, каков ваш приоритет.

+0

Я не могу использовать v (a.size()), потому что мой реальный элемент контейнера не является int, некоторым большим классом. и мне нужны две линии. – Khurshid

+0

@ Хуршид: Конечно, это зависит от вас. Здесь у вас есть все возможности. Что вы подразумеваете под требованием только двух строк? Зачем? Пространство в исходном коде очень дешево! :) –

+0

@ Khurshid Тем не менее, вызов 'reserve' + пример' bqck_inserter' кажется прекрасным вариантом. – juanchopanza

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