3

我正在制作一个“连接迭代器”,即一个迭代器,它将迭代ints 中的 s int**

它的构造函数需要:

  • 的数组T**,表示每个子数组的开头。
  • 的数组T**,表示每个子数组的结尾。

瞧,我遇到了一个goto似乎合适的情况。

但我内心的某种东西尖叫着“不!!” 所以我想我会来这里问:

我应该尽量避免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_it并且m_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_subEndm_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);

如果您不想进行两次比较并且仍然避免使用 goto 的隐形表亲之一,请中断或继续:

    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 hey 的主要问题是它迫使程序员记住代码中潜在的非逻辑运动——突然之间,代码几乎可以跳转到任何地方。如果您使用控制结构,即使您必须引入一些额外的行或诸如此类的东西,程序也会继续按预期运行并遵循流程。从长远来看,这就是可读性的全部意义所在。

于 2012-08-12T09:17:53.410 回答
2

不要使用 goto。唯一可以原谅 goto 的情况是,如果您有一个复杂的函数(无论如何都不应该有),并且您希望在函数末尾有一个集中的退出/清理部分,您可以在其中转到不同的错误在功能的不同部分,或因成功而失败。

总而言之,您应该在这里使用 do-while 循环。

于 2012-08-12T09:25:09.250 回答
0

人们使用汇编程序(和高级汇编程序)创建了中高级编译器。汇编器有许多 jmp jnz jg jl 命令的行为类似于 goto。他们做到了这一点。你不能这样做吗?如果你不能,那么你回答了你自己的问题。

我不能对口译员说同样的话。

于 2012-08-12T09:17:45.983 回答