これが本当に必要だとは思いません: 代入が高価な場合、型は (比較的安価な) 自己代入チェックを実行する代入演算子を定義して、不要な作業を防ぐ必要があります。しかし、これは興味深い質問であり、多くの落とし穴があるため、回答することに挑戦します。
入力イテレータと出力イテレータで機能する一般的なソリューションを組み立てる場合、注意しなければならない落とし穴がいくつかあります。
入力イテレータはシングルパス イテレータです。イテレータを介して間接参照を実行できるのは、要素ごとに 1 回だけです。そのため、イテレータを介して間接参照を 1 回実行して、ポイント先の値のアドレスを取得し、2 回目に実行することはできません。コピー。
入力反復子はプロキシ反復子の場合があります。プロキシ イテレータはoperator*
、参照ではなくオブジェクトを返すイテレータです。プロキシ イテレータを使用すると、 が右辺値であるため、式&*it
の形式が正しくありません*it
( unary- をオーバーロードすることは可能です&
が、そうすることは通常、悪で恐ろしいと見なされ、ほとんどの型はこれを行いません)。
出力反復子は出力にのみ使用できます。それを介して間接化を実行し、結果を右辺値として使用することはできません。「指された要素」に書き込むことはできますが、そこから読み取ることはできません。
したがって、「最適化」を行う場合は、両方の反復子が前方反復子である場合にのみ行う必要があります (これには、双方向反復子とランダム アクセス反復子が含まれます。それらも前方反復子です)。
私たちは親切なので、概念要件に違反しているという事実にもかかわらず、多くのプロキシ反復子がカテゴリを誤って伝えているという事実にも注意する必要があります。プロキシされたオブジェクトの。(これを行わずに効率的なイテレータを実装する方法もわかりませんstd::vector<bool>
。)
次の標準ライブラリ ヘッダーを使用します。
#include <iterator>
#include <type_traits>
#include <utility>
is_forward_iterator
型が「実際の」前方反復子であるかどうか (つまり、プロキシ反復子ではないかどうか) をテストするメタ関数 を定義します。
template <typename T>
struct is_forward_iterator :
std::integral_constant<
bool,
std::is_base_of<
std::forward_iterator_tag,
typename std::iterator_traits<T>::iterator_category
>::value &&
std::is_lvalue_reference<
decltype(*std::declval<T>())
>::value>
{ };
can_compare
簡潔にするために、2 つの型が両方とも前方反復子であるかどうかをテストするメタ関数 も定義します。
template <typename T, typename U>
struct can_compare :
std::integral_constant<
bool,
is_forward_iterator<T>::value &&
is_forward_iterator<U>::value
>
{ };
次に、copy
関数の 2 つのオーバーロードを記述し、SFINAE を使用して反復子の型に基づいて適切なオーバーロードを選択します。両方の反復子が前方反復子である場合はチェックを含め、そうでない場合はチェックを除外して常に実行します割り当て:
template <typename InputIt, typename OutputIt>
auto copy(InputIt const in, OutputIt const out)
-> typename std::enable_if<can_compare<InputIt, OutputIt>::value>::type
{
if (static_cast<void const volatile*>(std::addressof(*in)) !=
static_cast<void const volatile*>(std::addressof(*out)))
*out = *in;
}
template <typename InputIt, typename OutputIt>
auto copy(InputIt const in, OutputIt const out)
-> typename std::enable_if<!can_compare<InputIt, OutputIt>::value>::type
{
*out = *in;
}
パイと同じくらい簡単!