std :: vectorのデータポインタを変更できないのはなぜですか?

Dec 30 2020

配列char* sourceとベクトルがありstd::vector<char> targetます。データをコピーせずtargetsource、O(1)でベクトルが指すようにしたい。

これらの線に沿った何か:

#include <vector>

char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);

target.setData(source); // <- Doesn't exist
// OR
std::swap(target.data(), source); // <- swap() does not support char*
delete[] source;

ベクトルが指す場所を手動で変更できないのはなぜですか?これが可能である場合に発生する特定の管理不可能な問題はありますか?

回答

7 anatolyg Dec 30 2020 at 17:39

C ++vectorクラスは、要素の追加と削除をサポートし、メモリ内での連続した順序が保証されています。vector既存のメモリバッファで初期化し、それに十分な要素を追加できる場合、オーバーフローするか、再割り当てが必要になります。

のインターフェイスは、内部バッファを管理vectorしていることを前提としています。つまり、必要なときにいつでも(もちろん仕様の範囲内で)割り当て、割り当て解除、サイズ変更を行うことができます。バッファの管理が許可されていないものが必要な場合は、使用できませんvector。別のデータ構造を使用するか、自分で記述してください。

vector(2つのポインターまたはを使用したコンストラクターを使用して)データをコピーすることでオブジェクトを作成できますassignが、これは明らかにあなたが望むものではありません。

または、を使用することもできますstring_view。これは、ほぼまたは正確に必要なものに見えます。

3 NutCracker Dec 30 2020 at 19:23

std::vector基になるバッファの所有者と見なされます。バッファを変更することはできますが、この変更により割り当てが発生します。つまり、不要なソースバッファのコピーが作成されます(質問に記載されています)。

次のことができます。


#include <vector>

int main() {
    char* source = new char[3] { 1, 2, 3 };
    std::vector<char> target;
    target.resize(3);
    target.assign(source, source + 3);
    delete[] source;
    return 0;
}

しかし再びstd::vector::assign:

内容を[first、last)の範囲の内容のコピーに置き換えます。

そのため、コピーが再度実行されます。を使用している間は、それから逃れることはできませんstd::vector

データをコピーしたくない場合はstd::span、C ++ 20から使用する(または独自のスパンを作成する)か、std::string_viewcharsの配列があるため適切に見える)を使用する必要があります。

最初のオプション:使用 std::string_view

あなたはC ++ 17に制限されているので、あなたstd::string_viewにぴったりかもしれません。で指定された要素で始まる文字配列の最初の3文字のビューを作成しますsource

#include <iostream>
#include <string_view>

int main() {
    char* source = new char[3] { 1, 2, 3 };

    std::string_view strv( source, 3 );

    delete[] source;

    return 0;
}

2番目のオプション:std::spanC ++ 20から使用

std::spanC ++ 20から来ているので、それはあなたにとって最も完璧な方法ではないかもしれませんが、それが何であるか、そしてそれがどのように機能するかに興味があるかもしれません。これは、文字だけでなく、あらゆるタイプのオブジェクトの連続したシーケンスであるためstd::span、の少し一般化されたバージョンと考えることができますstd::string_view。使用法はstd::string_view:と同様です。

#include <span>
#include <iostream>

int main() {
    char* source = new char[3] { 1, 2, 3 };

    std::span s( source, 3 );

    delete[] source;

    return 0;
}

3番目のオプション:あなた自身のスパン

C ++ 17に制限されている場合は、独自のspan構造体を作成することを考えることができます。それでもやり過ぎかもしれませんが、お見せしましょう(ところで、このより手の込んだ答えを見てください):

template<typename T>
class span {
   T* ptr_;
   std::size_t len_;

public:
    span(T* ptr, std::size_t len) noexcept
        : ptr_{ptr}, len_{len}
    {}

    T& operator[](int i) noexcept {
        return *ptr_[i];
    }

    T const& operator[](int i) const noexcept {
        return *ptr_[i];
    }

    std::size_t size() const noexcept {
        return len_;
    }

    T* begin() noexcept {
        return ptr_;
    }

    T* end() noexcept {
        return ptr_ + len_;
    }
};

int main() {
    char* source = new char[3] { 1, 2, 3 };

    span s( source, 3 );

    delete[] source;

    return 0;
}

したがって、使用法はC ++ 20のバージョンのと同じですstd::span