4


0

閉じた範囲でのループ

このコードをどのように修正しますか?

template  void closed_range(T begin, T end)
{
    for (T i = begin; i <= end; ++i) {
        // do something
    }
}
  • Tは整数型に制限されており、そのような型の幅を広くすることができます 署名済みまたは未署名にすることができます

  • 「begin」は「numeric_limits :: min()」にすることができます

  • 「end」は「numeric_limits :: max()」にすることができます(この場合、「++ i」は 上記のコードでオーバーフロー)

いくつかの方法がありますが、私が本当に好きな方法はありません。

5 回答


6


多分、

template  void closed_range(T begin, const T end)
    if (begin <= end) {
        do {
            // do something
        } while (begin != end && (++begin, true));
    }
}

呪い、私の最初の試みは間違っていた、と上記の修正は私が期待したほどきれいではありません。 どうですか?

template  bool advance(T &value) { ++value; return true; }

template  void closed_range(T first, const T last)
    if (first <= last) {
        do {
            // do something
        } while (first != last && advance(first));
    }
}
Tが整数型でなくても、 `std

advance`は2つのパラメーターを受け取るため、` std :: advance`にはあいまいさはありません。 したがって、何らかの理由でそれらの閉じた範囲が必要な場合、テンプレートは、たとえばランダムアクセスイテレータでも動作します。

それとも、ちょっとした集合論はどうですか? 閉じた範囲でループを1つだけ記述している場合、これは明らかに過剰なやり過ぎです。しかし、もしそれが多くのことをしたい場合は、ループコードが適切になります。 効率についてはわかりません:本当にタイトなループでは、 `endof`への呼び出しがホイストされていることを確認したいかもしれません:

#include
#include

template
struct omega {
    T val;
    bool isInfinite;
    operator T() { return val; }
    explicit omega(const T &v) : val(v), isInfinite(false) { }
    omega &operator++() {
        (val == std::numeric_limits::max()) ? isInfinite = true : ++val;
        return *this;
    }
};

template
bool operator==(const omega &lhs, const omega &rhs) {
    if (lhs.isInfinite) return rhs.isInfinite;
    return (!rhs.isInfinite) && lhs.val == rhs.val;
}
template
bool operator!=(const omega &lhs, const omega &rhs) {
    return !(lhs == rhs);
}

template
omega endof(T val) {
    omega e(val);
    return ++e;
}

template
void closed_range(T first, T last) {
    for (omega i(first); i != endof(last); ++i) {
        // do something
        std::cout << i << "\n";
    }
}

int main() {
    closed_range((short)32765, std::numeric_limits::max());
    closed_range((unsigned short)65533, std::numeric_limits::max());
    closed_range(1, 0);
}

出力:

32765
32766
32767
65533
65534
65535

「omega」オブジェクトで他の演算子を使用する場合は注意が必要です。 デモンストレーションの絶対最小値を実装しただけで、 omega`は暗黙的に T`に変換されるため、潜在的にオメガオブジェクトの「無限」を捨てる式を書くことができます。 これを修正するには、算術演算子の完全なセットを宣言する(必ずしも定義する必要はありません);またはisInfiniteがtrueの場合、変換で例外をスローします。または、コンストラクタが明示的であるため、誤って結果をオメガに戻すことができないという理由で心配しないでください。 しかし、たとえば、 `omega(2)<endof(2)`はtrueですが、 `omega(INT_MAX)<endof(INT_MAX)`はfalseです。


5


私の考え:

// Make sure we have at least one iteration
if (begin <= end)
{
    for (T i = begin; ; ++i)
    {
        // do something

        // Check at the end *before* incrementing so this won't
        // be affected by overflow
        if (i == end)
            break;
    }
}


4


これは機能し、かなり明確です:

T i = begin;
do {
   ...
}
while (i++ < end);

begin> = end`の特殊なケースをキャッチしたい場合は、Steve Jessopのソリューションのように別の if`を追加する必要があります。


3


template
void closed_range(T begin, T end, F functionToPerform)
{
    for (T i = begin; i != end; ++i) {
        functionToPerform(i);
    }
    functionToPerform(end);
}


0


編集:OPにより厳密に一致するように物事を作り直しました。

#include
using namespace std;

template void closed_range(T begin, T end)
{
    for( bool cont = (begin <= end); cont; )
    {
        // do something
        cout << begin << ", ";
        if( begin == end )
            cont = false;
        else
            ++begin;
    }

    // test - this should return the last element
    cout << " --  " << begin;
}
int main()
{
    closed_range(10, 20);
    return 0;
}

出力は以下のとおりです。

_ 10、11、12、13、14、15、16、16、17、18、19、20、-20 _