C++ | vector を結合する(コピー方式・ムーブ方式)

C++ で vector を結合する方法を紹介します。

目次

コピーで vector を結合する

まずは要素のコピーで vector を結合する方法です。
vector の要素が次に該当する場合は、この手法を用いるのが良いです。

  • 要素の型が shared_ptr である場合
  • 要素が軽量であり、かつコピー可である場合(例えば int や float など)

insert メソッド(要素のコピー)

vector クラスの insert メソッドで結合する例です。
v1 の末尾に v2 の各要素をコピーします。

C++
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v1{0, 1, 2};
    std::vector<int> v2{3, 4};

    v1.insert(v1.end(), v2.begin(), v2.end());

    for (auto item : v1) {
        std::cout << item << ", ";
    }
    std::cout << std::endl;
    
    // output
    // 0, 1, 2, 3, 4, 

    return 0;
}

std::copy(要素のコピー)

std::copy を用いることでも同様の効果を得ることができます。
前の例と同じように、v1 の末尾に v2 の要素がコピーされます。

C++
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v1{0, 1, 2};
    std::vector<int> v2{3, 4};

    std::copy(v2.begin(), v2.end(), std::back_inserter(v1));

    for (auto item : v1) {
        std::cout << item << ", ";
    }
    std::cout << std::endl;

    // output
    // 0, 1, 2, 3, 4, 

    return 0;
}

std::merge(要素のコピー)

std::merge を用いると、v1, v2 の状態を保全したまま新規の vector を生成することができます。

C++
#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v1{0, 1, 2};
    std::vector<int> v2{3, 4};
    std::vector<int> v3;

    std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(v3));

    for (auto item : v3) {
        std::cout << item << ", ";
    }
    std::cout << std::endl;

    // output
    // 0, 1, 2, 3, 4, 

    return 0;
}

ムーブで vector を結合する

コピーの例ではいくつか問題が発生することがあります。

vector の要素のサイズが大きい場合はパフォーマンスの問題が発生し得ます。
コピー不可の型が要素である場合には、そもそもコンパイルでエラーが発生してしまいます。

例えば、unique_ptr(コピー不可の型)に対して insert を行った場合、g++ のコンパイルではおびただしい量のエラーメッセージが出力されます。

main_move.cc
#include <iostream>
#include <memory>
#include <vector>

int main() {
    std::vector<std::unique_ptr<int>> v1;
    v1.push_back(std::make_unique<int>(0));
    v1.push_back(std::make_unique<int>(1));
    v1.push_back(std::make_unique<int>(2));

    std::vector<std::unique_ptr<int>> v2;
    v2.push_back(std::make_unique<int>(3));
    v2.push_back(std::make_unique<int>(4));

    v1.insert(v1.end(), v2.begin(), v2.end());
    
    return 0;
}
コンパイルのエラーメッセージ
$ g++ main_move.cc
In file included from /usr/include/c++/11/memory:66,
                 from main_move.cc:2:
/usr/include/c++/11/bits/stl_uninitialized.h: In instantiation of ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _ForwardIterator = std::unique_ptr<int>*]’:
/usr/include/c++/11/bits/stl_uninitialized.h:333:37:   required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _ForwardIterator = std::unique_ptr<int>*; _Tp = std::unique_ptr<int>]’
/usr/include/c++/11/bits/vector.tcc:751:34:   required from ‘void std::vector<_Tp, _Alloc>::_M_range_insert(std::vector<_Tp, _Alloc>::iterator, _ForwardIterator, _ForwardIterator, std::forward_iterator_tag) [with _ForwardIterator = __gnu_cxx::__normal_iterator<std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _Tp = std::unique_ptr<int>; _Alloc = std::allocator<std::unique_ptr<int> >; std::vector<_Tp, _Alloc>::iterator = std::vector<std::unique_ptr<int> >::iterator]’
/usr/include/c++/11/bits/stl_vector.h:1665:19:   required from ‘void std::vector<_Tp, _Alloc>::_M_insert_dispatch(std::vector<_Tp, _Alloc>::iterator, _InputIterator, _InputIterator, std::__false_type) [with _InputIterator = __gnu_cxx::__normal_iterator<std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _Tp = std::unique_ptr<int>; _Alloc = std::allocator<std::unique_ptr<int> >; std::vector<_Tp, _Alloc>::iterator = std::vector<std::unique_ptr<int> >::iterator]’
/usr/include/c++/11/bits/stl_vector.h:1383:22:   required from ‘std::vector<_Tp, _Alloc>::iterator std::vector<_Tp, _Alloc>::insert(std::vector<_Tp, _Alloc>::const_iterator, _InputIterator, _InputIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; <template-parameter-2-2> = void; _Tp = std::unique_ptr<int>; _Alloc = std::allocator<std::unique_ptr<int> >; std::vector<_Tp, _Alloc>::iterator = std::vector<std::unique_ptr<int> >::iterator; std::vector<_Tp, _Alloc>::const_iterator = std::vector<std::unique_ptr<int> >::const_iterator]’
main_move.cc:15:14:   required from here
/usr/include/c++/11/bits/stl_uninitialized.h:138:72: error: static assertion failed: result type must be constructible from value type of input range
  138 |       static_assert(is_constructible<_ValueType2, decltype(*__first)>::value,
      |                                                                        ^~~~~
/usr/include/c++/11/bits/stl_uninitialized.h:138:72: note: ‘std::integral_constant<bool, false>::value’ evaluates to false
In file included from /usr/include/c++/11/bits/char_traits.h:39,
                 from /usr/include/c++/11/ios:40,
                 from /usr/include/c++/11/ostream:38,
                 from /usr/include/c++/11/iostream:39,
                 from main_move.cc:1:
/usr/include/c++/11/bits/stl_algobase.h: In instantiation of ‘static _OI std::__copy_move<false, false, std::random_access_iterator_tag>::__copy_m(_II, _II, _OI) [with _II = std::unique_ptr<int>*; _OI = std::unique_ptr<int>*]’:
/usr/include/c++/11/bits/stl_algobase.h:495:30:   required from ‘_OI std::__copy_move_a2(_II, _II, _OI) [with bool _IsMove = false; _II = std::unique_ptr<int>*; _OI = std::unique_ptr<int>*]’
/usr/include/c++/11/bits/stl_algobase.h:522:42:   required from ‘_OI std::__copy_move_a1(_II, _II, _OI) [with bool _IsMove = false; _II = std::unique_ptr<int>*; _OI = std::unique_ptr<int>*]’
/usr/include/c++/11/bits/stl_algobase.h:530:31:   required from ‘_OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = false; _II = __gnu_cxx::__normal_iterator<std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _OI = __gnu_cxx::__normal_iterator<std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >]’
/usr/include/c++/11/bits/stl_algobase.h:620:7:   required from ‘_OI std::copy(_II, _II, _OI) [with _II = __gnu_cxx::__normal_iterator<std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _OI = __gnu_cxx::__normal_iterator<std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >]’
/usr/include/c++/11/bits/vector.tcc:744:16:   required from ‘void std::vector<_Tp, _Alloc>::_M_range_insert(std::vector<_Tp, _Alloc>::iterator, _ForwardIterator, _ForwardIterator, std::forward_iterator_tag) [with _ForwardIterator = __gnu_cxx::__normal_iterator<std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _Tp = std::unique_ptr<int>; _Alloc = std::allocator<std::unique_ptr<int> >; std::vector<_Tp, _Alloc>::iterator = std::vector<std::unique_ptr<int> >::iterator]’
/usr/include/c++/11/bits/stl_vector.h:1665:19:   required from ‘void std::vector<_Tp, _Alloc>::_M_insert_dispatch(std::vector<_Tp, _Alloc>::iterator, _InputIterator, _InputIterator, std::__false_type) [with _InputIterator = __gnu_cxx::__normal_iterator<std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _Tp = std::unique_ptr<int>; _Alloc = std::allocator<std::unique_ptr<int> >; std::vector<_Tp, _Alloc>::iterator = std::vector<std::unique_ptr<int> >::iterator]’
/usr/include/c++/11/bits/stl_vector.h:1383:22:   required from ‘std::vector<_Tp, _Alloc>::iterator std::vector<_Tp, _Alloc>::insert(std::vector<_Tp, _Alloc>::const_iterator, _InputIterator, _InputIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; <template-parameter-2-2> = void; _Tp = std::unique_ptr<int>; _Alloc = std::allocator<std::unique_ptr<int> >; std::vector<_Tp, _Alloc>::iterator = std::vector<std::unique_ptr<int> >::iterator; std::vector<_Tp, _Alloc>::const_iterator = std::vector<std::unique_ptr<int> >::const_iterator]’
main_move.cc:15:14:   required from here
/usr/include/c++/11/bits/stl_algobase.h:385:25: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’
  385 |               *__result = *__first;
      |               ~~~~~~~~~~^~~~~~~~~~
In file included from /usr/include/c++/11/memory:76,
                 from main_move.cc:2:
/usr/include/c++/11/bits/unique_ptr.h:469:19: note: declared here
  469 |       unique_ptr& operator=(const unique_ptr&) = delete;
      |                   ^~~~~~~~

この問題を解決するために、値のムーブによって vector を結合する手法を後述します。

insert メソッド(要素のムーブ)

メソッドについては、コピー方式と同じものを利用します。
異なる点として、v2 のイテレータをムーブイテレータに変換します。

具体的には、insert メソッドの第 2, 3 引数を std::make_move_iterator でラップさせてやります。

C++
#include <iostream>
#include <memory>
#include <vector>

int main() {
    std::vector<std::unique_ptr<int>> v1;
    v1.push_back(std::make_unique<int>(0));
    v1.push_back(std::make_unique<int>(1));
    v1.push_back(std::make_unique<int>(2));

    std::vector<std::unique_ptr<int>> v2;
    v2.push_back(std::make_unique<int>(3));
    v2.push_back(std::make_unique<int>(4));

    v1.insert(
        v1.end(),
        std::make_move_iterator(v2.begin()),
        std::make_move_iterator(v2.end()));

    for (const auto &item : v1) {
        std::cout << *item << ", ";
    }
    std::cout << std::endl;

    // output
    // 0, 1, 2, 3, 4, 
    
    return 0;
}

注意点として、ムーブした後は v2 の要素は無効な状態として更新されてしまいます。
例えば、次のように insert 後に v2 の要素にアクセスしようとすると Segmentation fault(メモリの不正アクセス)によってプログラムが中断されてしまいます。

C++
#include <iostream>
#include <memory>
#include <vector>

int main() {
    std::vector<std::unique_ptr<int>> v1;
    v1.push_back(std::make_unique<int>(0));
    v1.push_back(std::make_unique<int>(1));
    v1.push_back(std::make_unique<int>(2));

    std::vector<std::unique_ptr<int>> v2;
    v2.push_back(std::make_unique<int>(3));
    v2.push_back(std::make_unique<int>(4));

    v1.insert(
        v1.end(),
        std::make_move_iterator(v2.begin()),
        std::make_move_iterator(v2.end()));

    std::cout << *v2[0] << std::endl;
    // Segmentation fault

    return 0;
}

std::copy(要素のムーブ)

std::copy のケースも make_move_iterator を呼び出すだけで大丈夫です。
insert 同様に、エラーが発生することなく vector の統合が可能です。

C++
#include <iostream>
#include <memory>
#include <vector>

int main() {
    std::vector<std::unique_ptr<int>> v1;
    v1.push_back(std::make_unique<int>(0));
    v1.push_back(std::make_unique<int>(1));
    v1.push_back(std::make_unique<int>(2));

    std::vector<std::unique_ptr<int>> v2;
    v2.push_back(std::make_unique<int>(3));
    v2.push_back(std::make_unique<int>(4));

    std::copy(
        std::make_move_iterator(v2.begin()),
        std::make_move_iterator(v2.end()),
        std::back_inserter(v1));

    for (const auto &item : v1) {
        std::cout << *item << ", ";
    }
    std::cout << std::endl;

    // output
    // 0, 1, 2, 3, 4, 
    
    return 0;
}

std::merge(要素のムーブ)

あえて掲載するまでもないかもしれないですが、std::merge でムーブを行う例です。
v1 と v2 の要素を v3 にムーブして統合します。

C++
#include <algorithm>
#include <iostream>
#include <memory>
#include <vector>

int main() {
    std::vector<std::unique_ptr<int>> v1;
    v1.push_back(std::make_unique<int>(0));
    v1.push_back(std::make_unique<int>(1));
    v1.push_back(std::make_unique<int>(2));

    std::vector<std::unique_ptr<int>> v2;
    v2.push_back(std::make_unique<int>(3));
    v2.push_back(std::make_unique<int>(4));

    std::vector<std::unique_ptr<int>> v3;

    std::merge(
        std::make_move_iterator(v1.begin()),
        std::make_move_iterator(v1.end()),
        std::make_move_iterator(v2.begin()),
        std::make_move_iterator(v2.end()),
        std::back_inserter(v3));

    for (const auto &item : v3) {
        std::cout << *item << ", ";
    }
    std::cout << std::endl;

    // output
    // 0, 1, 2, 3, 4, 

    return 0;
}
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次