2013-02-08 75 views
3

假設我有一個包含「a」和「b」的vector<string>,我想複製它自己2次,以便現在包含「a」,「b」,「a」,「b」,「a」,「 b「如何自我複製矢量?

什麼是比使用forpush_back更好的方法?

+0

剛需討厭:的foreach – odinthenerd 2013-02-08 21:02:27

+0

emplace_back可能會使用std ::副本會比更快的for循環 – odinthenerd 2013-02-08 21:02:55

+0

和memset兩次 – odinthenerd 2013-02-08 21:03:29

回答

4

我最初的想法:

myvec.reserve(myvec.size()*3); //reserve not only speeds things upt it also protects us ftom iterator invalidation 
vector<string>::iterator it = myvec.end(); //we ant to add two times the origional onto the end so we save the end of the origional end 
myvec.insert(myvec.end(), myvec.begin(), it); 
myvec.insert(myvec.end(), myvec.begin(), it); 

感謝埃米利奧加拉瓦利亞爲第一指出這個問題,在這裏看到很多原因,這有問題:Does std::vector::insert() invalidate iterators if the vector has enough room (created through reserve)?

第二個嘗試:

std::size_t size = myvec.size(); 
myvec.resize(size*3); //resize must protects us from iterator invalidation 
vector<string>::iterator it = myvec.begin()+size; 
std::copy(myvec.begin(),it,it); 
std::copy(myvec.begin(),it,it+size); 

因爲沒有實現將實現一個std :: string whos默認的構造函數在堆上分配的東西,這應該會導致更少的堆訪問因此比其他例子更快。

另一個堆訪問最小化是將載體複製到另一個插入,然後在原稿動,我偷了埃米利奧加拉瓦利亞代碼和拉皮條它:

{ 
vector<string> m = { "a", "b" }; 
auto n = m; // keep initial value safe 
m.reserve(3 * m.size()); // preallocate, to avoid consecutive allocations 
m.insert(m.end, n.begin(), n.end()); 
std::for_each(n.begin(),n.end(),[&n](std::string &in){n.emplace_back(std::move(in));}); 
} 
+0

在C++ 11中自動生成嗎? – NSF 2013-02-08 21:12:45

+0

@NSF是的,至少在目前這種用法下。 – Rapptz 2013-02-08 21:13:46

+0

按照標準,修改後的矢量上的迭代器應始終被視爲無效。你所說的在大多數實現中都是真實的,但是標準並沒有把它作爲一個要求,因此我們不能相信它會一直工作。 – 2013-02-08 21:14:46

2
vector<string> m = { "a", "b" }; 

auto n = m; // keep initial value safe 

m.reserve(3 * m.size()); // preallocate, to avoid consecutive allocations 
m.insert(m.end, n.begin(), n.end()); 
m.insert(m.end, n.begin(), n.end()); 
+0

保存第二次插入,從n中交換值,這將防止您在堆上分配一堆字符串緩衝區,複製內容,然後拋棄原始文件。 – odinthenerd 2013-02-08 21:35:55

+0

+1指出我的錯誤較早,因爲我偷了,只是稍微修改了我的第三個例子 – odinthenerd 2013-02-11 22:03:38

2

我首先想到的是以下內容避免使迭代器失效的問題。

{ // note: nested scope 
    vector<string> temp(vec); // 1st copy 
    temp.insert(temp.end(), vec.begin(), vec.end()); // 2nd copy 
    temp.insert(temp.end(), vec.begin(), vec.end()); // 3rd copy 
    temp.swap(vec); // swap contents before temp is destroyed 
} 

經過審查,我認爲PorkyBrain和埃米利奧加拉瓦利亞的答案可能更有意義。

+1

Emilio Garavaglia的答案和你的答案是一樣的(除了'reserve'調用,這是可選的)。你有更強的異常安全保證(如果第二個'insert'失敗,你的代碼不會改變原來的向量),所以我給它+1 :) – Alex 2013-02-08 21:26:27

+0

如果向量必須在它的中間增長將變慢,保留失蹤。 – odinthenerd 2013-02-08 21:34:37

+0

@PorkyBrain:這是分期固定的時間,所以我想在嘗試野蠻優化的替代方案之前查看它是否是實際的瓶頸。 – Blastfurnace 2013-02-08 21:39:00

1

下面是一個簡單的方法:

template<typename T, typename A1, typename A2> 
std::vector<T, A1> operator+(std::vector<T, A1> left, std::vector<T, A2> const& right) { 
    left.insert(left.end(), right.begin(), right.end()); 
    return left; 
} 


int main() { 
    std::vector<string> m = { "a", "b"); 

    m = m + m + m; 
} 

但作爲@ChristianAmmer所指出的,在一個std::vector重寫operator+是不明確的。這將是錯誤的。所以你可以寫一個完整的中綴命名操作符語法,並使用C++的魔法將其嵌入到C++語言中,以消除這種模糊性。排序是這樣的:

#include <utility> 

template<typename Operation, short op> 
struct InfixOp { 
    Operation* self() { return static_cast<Operation*>(this); } 
    Operation const* self() const { return static_cast<Operation const*>(this); } 
}; 

template<typename first_type, typename Infix, short op> 
struct PartialForm { 
    Infix const* infix; 

    first_type a; 

    template<typename T> 
    PartialForm(T&& first, Infix const* i):infix(i), a(std::forward<T>(first)) {} 
}; 

#define OVERRIDE_OPERATORS(OP, CODE) \ 
template<\ 
    typename Left,\ 
    typename Operation\ 
>\ 
PartialForm<typename std::remove_reference<Left>::type, Operation, CODE> operator OP(Left&& left, InfixOp<Operation, CODE> const& op) {\ 
    return PartialForm<typename std::remove_reference<Left>::type, Operation, CODE>(std::forward<Left>(left), op.self());\ 
}\ 
\ 
template<\ 
    typename Right,\ 
    typename First,\ 
    typename Operation\ 
>\ 
auto operator OP(PartialForm<First, Operation, CODE>&& left, Right&& right)\ 
    ->decltype((*left.infix)(std::move(left.a), std::forward<Right>(right)))\ 
{\ 
    return (*left.infix)(std::move(left.a), std::forward<Right>(right));\ 
} 

OVERRIDE_OPERATORS(+, '+') 
OVERRIDE_OPERATORS(*, '*') 
OVERRIDE_OPERATORS(%, '%') 
OVERRIDE_OPERATORS(^, '^') 
OVERRIDE_OPERATORS(/, '/') 
OVERRIDE_OPERATORS(==, '=') 
OVERRIDE_OPERATORS(<, '<') 
OVERRIDE_OPERATORS(>, '>') 
OVERRIDE_OPERATORS(&, '&') 
OVERRIDE_OPERATORS(|, '|') 
//OVERRIDE_OPERATORS(!=, '!=') 
//OVERRIDE_OPERATORS(<=, '<=') 
//OVERRIDE_OPERATORS(>=, '>=') 


template<typename Functor, char... ops> 
struct Infix:InfixOp<Infix<Functor, ops...>, ops>... 
{ 
    Functor f; 
    template<typename F> 
    explicit Infix(F&& f_in):f(std::forward<F>(f_in)) {} 
    Infix(Infix<Functor, ops...> const& o):f(o.f) {} 
    Infix(Infix<Functor, ops...>&& o):f(std::move(o.f)) {} 
    Infix(Infix<Functor, ops...>& o):f(o.f) {} 
    template<typename L, typename R> 
    auto operator()(L&& left, R&& right) const 
    -> decltype(f(std::forward<L>(left), std::forward<R>(right))) 
    { 
    return f(std::forward<L>(left), std::forward<R>(right)); 
    } 
}; 

template<char... ops, typename Functor> 
Infix<Functor, ops...> make_infix(Functor&& f) 
{ 
    return Infix<Functor, ops...>(std::forward<Functor>(f)); 
} 

#include <vector> 

struct append_vectors { 
    template<typename T> 
    std::vector<T> operator()(std::vector<T> left, std::vector<T>const& right) const { 
    left.insert(left.end(), right.begin(), right.end()); 
    return std::move(left); 
    } 
}; 

struct sum_elements { 
    template<typename T> 
    std::vector<T> operator()(std::vector<T> left, std::vector<T>const& right) const { 
    for(auto it = left.begin(), it2 = right.begin(); it != left.end() && it2 != right.end(); ++it, ++it2) { 
     *it = *it + *it2; 
    } 
    return left; 
    } 
}; 
struct prod_elements { 
    template<typename T> 
    std::vector<T> operator()(std::vector<T> left, std::vector<T>const& right) const { 
    for(auto it = left.begin(), it2 = right.begin(); it != left.end() && it2 != right.end(); ++it, ++it2) { 
     *it = *it * *it2; 
    } 
    return left; 
    } 
}; 

#include <iostream> 

int main() { 
    auto append = make_infix<'+'>(append_vectors()); 
    auto sum = make_infix<'+'>(sum_elements()); 
    auto prod = make_infix<'*'>(prod_elements()); 

    std::vector<int> a = {1,2,3}; 
    a = a +append+ a +append+ a; 
    a = a +sum+ a; 
    a = a *prod* a; 

    std::cout << a.size() << "\n"; 
    for (auto&& x:a) { 
    std::cout << x << ","; 
    } 
    std::cout << "\n"; 
} 

其中有在您使用它的點清晰度的優勢(我的意思是,a = a +append+ a是很清楚什麼打算做),以作爲一個有點棘手,瞭解成本它是如何工作的,並且對於這樣一個簡單的問題有點冗長。

但至少模糊不見了,這是件好事,對嗎?

+0

爲'std :: vector'重載'operator + ='被認爲是[ambiguous](http://stackoverflow.com/q/6366231/237483),這個(在我看來)也適用於'operator +'。 – 2013-02-08 22:36:52

+0

這與我們一直在討論的迭代器有相同的問題。 – odinthenerd 2013-02-08 23:25:18

+0

@PorkyBrain我不這麼認爲。仔細檢查'left'的使用壽命。 – Yakk 2013-02-09 21:21:00