私の参照はstd :: copyとstd :: copy_backwardです。
template <class InputIt、class OutputIt> OutputIt copy(InputIt first、InputIt last、OutputIt d_first);
[first、last)の範囲内のすべての要素を、最初から最後までコピーします-1。d_firstが[first、last)の範囲内にある場合、動作は定義されません。この場合、代わりにstd :: copy_backwardを使用できます。
テンプレート<クラスBidirIt1、クラスBidirIt2> BidirIt2 copy_backward(BidirIt1が最初、BidirIt1が最後、BidirIt2 d_last)
[first、last)で定義された範囲から、d_lastで終わる別の範囲に要素をコピーします。要素は逆の順序でコピーされます(最後の要素が最初にコピーされます)が、それらの相対的な順序は保持されます。
d_lastが(first、last]内にある場合、動作は未定義です。その場合、std :: copy_backwardの代わりにstd :: copyを使用する必要があります。
重複する範囲をコピーする場合、左にコピーする場合はstd :: copyが適切であり(宛先範囲の先頭がソース範囲外)、右にコピーする場合はstd :: copy_backwardが適切です(宛先範囲の終わりがソース外です)。範囲)。
上記の説明から、私は次の推論を収集します。
copyとcopy_backwardはどちらも、同じソース範囲[first、last)を宛先範囲にコピーすることになります。ただし、前者の場合は最初から最後の-1までコピーが行われ、後者の場合は最後からコピーが行われます。 -1から最初。どちらの場合も、ソース範囲内の要素の相対的な順序は、結果の宛先範囲で保持されます。
ただし、次の2つの規定の背後にある技術的な理由は何ですか。
1)コピーの場合、d_firstが[first、last)の範囲内にあると、未定義の動作が発生します(ソース範囲から宛先範囲へのコピーが失敗し、システム障害が発生する可能性があります)。
2)copy_backwardの場合、d_lastが範囲(first、last]内にあると、未定義の動作が発生します(ソース範囲の宛先範囲へのコピーの失敗と、場合によってはシステム障害を意味します)。
上記の未定義の動作シナリオを回避するためにcopyをcopy_backwardに置き換えるという提案は、上記の2つのステートメントの意味を理解すると明らかになると思います。
同様に、左にコピーするときのコピーの適切性(この概念は私にはわかりません)、および右にコピーするときのcopy_backward(この概念も私にはわかりません)についての言及が始まると思います。上記のcopyとcopy_backwardの違いを理解したら、意味があります。
いつものようにあなたの役に立つ考えを楽しみにしています。
フォローアップとして、次のテストコードを記述して、同一の操作でcopyとcopy_backwardの両方の動作を検証しました。
#include <array>
#include <algorithm>
#include <cstddef>
#include <iostream>
using std::array;
using std::copy;
using std::copy_backward;
using std::size_t;
using std::cout;
using std::endl;
int main (void)
{
const size_t sz = 4;
array<int,sz>a1 = {0,1,2,3};
array<int,sz>a2 = {0,1,2,3};
cout << "Array1 before copy" << endl;
cout << "==================" << endl;
for(auto&& i : a1) //the type of i is int&
{
cout << i << endl;
}
copy(a1.begin(),a1.begin()+3,a1.begin()+1);
cout << "Array1 after copy" << endl;
cout << "=================" << endl;
for(auto&& i : a1) //the type of i is int&
{
cout << i << endl;
}
cout << "Array2 before copy backward" << endl;
cout << "===========================" << endl;
for(auto&& i : a2) //the type of i is int&
{
cout << i << endl;
}
copy_backward(a2.begin(),a2.begin()+3,a2.begin()+1);
cout << "Array2 after copy backward" << endl;
cout << "==========================" << endl;
for(auto&& i : a2) //the type of i is int&
{
cout << i << endl;
}
return (0);
}
プログラムの出力は次のとおりです。
Array1 before copy
==================
0
1
2
3
Array1 after copy
=================
0
0
1
2
Array2 before copy backward
===========================
0
1
2
3
Array2 after copy backward
==========================
2
1
2
3
明らかに、d_firstが[first、last)の範囲内にある場合でも、copyは期待される結果を生成しますが、copy_backwardは生成しません。さらに、d_lastも範囲(first、last]内にあるため、ドキュメントによると、copy_backwardの場合は未定義の動作になります。
したがって、実際には、プログラムの出力は、copy_backwardの場合はドキュメントに準拠していますが、copyの場合はそうではありません。
どちらの場合も、d_firstとd_lastは条件を満たしているため、ドキュメントに従って、copyとcopy_backwardの両方でそれぞれ未定義の動作が発生することに注意してください。ただし、未定義の動作は、copy_backwardの場合にのみ観察されます。
ここでは何も深くは起こっていません。単純なアプローチを使用して、サンプルデータでアルゴリズムのランスルーを実行するだけです。各要素を順番にコピーします。
4要素の配列がint a[4] = {0, 1, 2, 3}
あり、最初の3つの要素を最後の3つの要素にコピーするとします。理想的には、で終わるでしょう{0, 0, 1, 2}
。これはどのように機能しstd::copy(a, a+3, a+1)
ますか(機能しません)?
手順1:最初の要素をコピーするa[1] = a[0];
配列は次のようになり{0, 0, 2, 3}
ます。
手順2: 2番目の要素をコピーするa[2] = a[1];
配列は次のようになり{0, 0, 0, 3}
ます。
手順3: 3番目の要素をコピーするa[3] = a[2];
配列は次のようになり{0, 0, 0, 0}
ます。
これらの値を読み取る前にソースデータ(a[1]
およびa[2]
)の一部を上書きしたため、結果は間違っています。逆の順序では、値を上書きする前に読み取るため、逆のコピーは機能します。
1つの合理的なアプローチでは結果が間違っているため、標準では動作が「未定義」であると宣言されています。素朴なアプローチを取りたいコンパイラーはそうかもしれません、そして彼らはこの場合を説明する必要はありません。この場合、間違っていても大丈夫です。異なるアプローチをとるコンパイラーは、異なる結果を生成する可能性があり、おそらく「正しい」結果でさえあります。それもOKです。コンパイラにとって最も簡単なものは、標準では問題ありません。
質問の補遺に照らして:これは未定義の動作であることに注意してください。これは、動作がプログラマーの意図に反するものとして定義されていることを意味するものではありません。むしろ、動作がC ++標準で定義されていないことを意味します。何が起こるかを決めるのは各コンパイラー次第です。の結果はstd::copy(a, a+3, a+1)
何でもかまいません。の素朴な結果が得られる可能性があり{0, 0, 0, 0}
ます。ただし、代わりにの意図した結果が得られる場合があり{0, 0, 1, 2}
ます。他の結果も可能です。幸運にも意図した動作が得られたという理由だけで、未定義の動作がないと結論付けることはできません。未定義の動作で正しい結果が得られる場合があります。(これが、未定義の動作に関連するバグの追跡が非常に難しい理由の1つです。)
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加