3

「連結イテレータ」、つまり のints を反復するイテレータを作成していましたint**

そのコンストラクターには次が必要です。

  • T**各サブ配列の先頭を表す の配列。
  • T**各サブ配列の終わりを表す の配列。

見よ、私はgoto適切と思われる状況に出くわした.

しかし、私の中で何かが「NO!!」と叫びました。だから私はここに来て尋ねようと思った:

gotoこのような状況は避けるべきですか?(そうすれば可読性が向上しますか?)

#include <algorithm>

template<class T>
class lazy_concat_iterator
{
    // This code was meant to work for any valid input iterator
    // but for easier reading, I'll assume the type is: T**

    mutable T** m_endIt;              // points to an array of end-pointers
    mutable T** m_it;                 // points to an array of begin-pointers
    mutable bool m_started;   // have we started iterating?
    mutable T* m_sub;         // points somewhere in the current sub-array
    mutable T* m_subEnd;      // points to the end of the current sub-array

public:
    lazy_concat_iterator(T** begins, T** ends)
        : m_it(begins), m_endIt(ends), m_started(false) { }

    void ensure_started() const
    {
        if (!m_started)
        {
            m_started = true;
        
        INIT:
            m_sub = *m_it;
            m_subEnd = *m_endIt;

            if (m_sub == m_subEnd)  // End of this subarray?
            {
                ++m_it;
                ++m_endIt;
                goto INIT;  // try next one         <<< should I use goto here?
            }
        }
    }
};

どのように使用できますか:

#include <vector>
#include <cstring>

using namespace std;

int main(int argc, char* argv[])
{
    vector<char*> beginnings(argv, argv + argc);

    vector<char*> endings;
    for (int i = 0; i < argc; i++)
        endings.push_back(argv[i] + strlen(argv[i]));

    lazy_concat_iterator<char> it(&beginnings[0], &endings[0]);
    it.ensure_started();  // 'it' would call this internally, when dereferenced
}
4

5 に答える 5

11

はい、回避できますし、回避する必要があります。gotoたとえば、このコードは、ラベルから行うことと同等のことを行う必要があります(これは、逆参照せず、条件が満たされると余分な時間がかかるためINIT、「隠された要件」であった入力イテレーターでも機能します)。以前の変換とは異なります):m_itm_endIt

while ((m_subIt = *m_it) == (m_subEnd = *m_endIt))
{
    ++m_it;
    ++m_endIt;
}

以前の回答試行:

永久ループでさえ、goto. 明らかな「決して終了しない」可能性をさらに強調しています。

    for (;;)
    {
        m_sub = *m_it;
        m_subEnd = *m_endIt;

        if (m_sub != m_subEnd)
            break;

        ++m_it;
        ++m_endIt;
    }

m_subEndループ内およびループ内に割り当てる必要がある理由はわかりませんがm_subIt。そうでない場合は、これを while ループとして書き直すことができます。

while (*m_it == *m_endIt)
{
    ++m_it;
    ++m_endIt;
}

m_subIt = *m_it;
m_subEnd = *m_endIt;
于 2012-08-12T09:18:56.813 に答える
6
while (*m_it == *m_endIt)
{
    ++m_it;
    ++m_endIt;
}

m_sub = *m_it;
m_subEnd = *m_endIt;
于 2012-08-12T09:18:16.453 に答える
3

for ループはないかもしれませんが、do-while かもしれません。

    do {
        m_sub = *m_it;
        m_subEnd = *m_endIt;

        if (m_sub == m_subEnd)  // End of this subarray?
        {
            ++m_it;
            ++m_endIt;
        }
    } while (m_sub == m_subEnd);

比較を 2 回行いたくない場合でも、goto のステルス従兄弟の 1 つを使用したくない場合は、break または continue を使用します。

    bool anotherround = FALSE;
    do {
        m_sub = *m_it;
        m_subEnd = *m_endIt;

        anotherround = m_sub == m_subEnd
        if (anotherround)  // End of this subarray?
        {
            ++m_it;
            ++m_endIt;
        }
    } while (anotherround);

コンテキストに関する知識があれば、より良い変数名を発明できると確信していますが、それがアイデアです。

可読性に対する goto の影響について: 私にとって goto の主な問題は、プログラマーがコード内の潜在的な非論理的な動きを記憶しなければならないことです。突然、コードがほとんどどこにでもジャンプする可能性があります。制御構造を使用すると、余分な行などを導入する必要がある場合でも、プログラムは期待どおりに動作し続け、フローに従います。そして長い目で見れば、それが読みやすさのすべてです。

于 2012-08-12T09:17:53.410 に答える
2

gotoは使用しないでください。gotoが許される唯一のケースは、複雑な関数(とにかく持ってはいけない)があり、関数の最後に一元化された終了/クリーンアップ部分が必要な場合です。ここで、さまざまなエラーにgotoを実行できます。関数のさまざまな部分で、または成功すると失敗します。

全体として、ここではdo-whileループを使用する必要があります。

于 2012-08-12T09:25:09.250 に答える
0

人々は、アセンブラ(および高レベルアセンブラ)を使用して、中および高レベルのコンパイラを作成しました。アセンブラには多くの jmp jnz jg jl コマンドがあり、goto のように動作します。彼らはここまでやってきた。同じことはできませんか?できない場合は、自分の質問に答えたことになります。

通訳者に同じことは言えません。

于 2012-08-12T09:17:45.983 に答える