在这种情况下,我应该避免"goto"吗?
Should I avoid "goto" in situations like this?
我正在制作一个"连接迭代器",即一个迭代器,它将在int**
中迭代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
}
是的,您可以并且应该避免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_subEnd
和m_subIt
。如果你不这样做,你可以把它重写为一个while循环:
while (*m_it == *m_endIt)
{
++m_it;
++m_endIt;
}
m_subIt = *m_it;
m_subEnd = *m_endIt;
while (*m_it == *m_endIt)
{
++m_it;
++m_endIt;
}
m_sub = *m_it;
m_subEnd = *m_endIt;
也许没有循环,但也许需要一段时间?
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);
以你对上下文的了解,我相信你可以发明更好的varnames,但这就是这个想法。
关于 goto 对可读性的影响:对我来说,goto herey 的主要问题是它迫使程序员记住代码中潜在的非逻辑运动 - 突然之间,代码几乎可以在任何地方跳跃。 如果您使用控制结构,即使您必须引入一些额外的行或其他内容,程序也会继续按预期运行并遵循流程。 从长远来看,这就是可读性的全部意义所在。
不要使用 goto。唯一可以原谅goto的情况是,如果你有一个复杂的函数(无论如何你不应该有),并且你想在函数的末尾有一个集中的退出/清理部分,在那里你可以去不同的错误在函数的不同部分,或者在成功时失败。
总而言之,您应该在此处使用do-while循环。
人们使用汇编程序(和高级汇编程序)创建了中高级编译器。汇编程序有许多jmp jnz jg jl命令的行为类似于goto。他们走到了这一步。你不能做同样的事情吗?如果你不能,那么你回答了你自己的问题。
我不能对口译员说同样的话。
- 我是c ++的新手,你能解释一下在这种情况下的指针吗
- 在这种情况下,java对象是否可以调用本机函数
- 在这种情况下显式调用时,std::cout 如何更改析构函数的行为?
- 在这种情况下,我真的复制了字节还是复制了字符?
- 为什么在这种情况下,bool 类型的输出等于 0?
- 在这种情况下,如何传递成员函数而不是函数?
- 为什么在这种情况下递增阵列名称有效?
- 在这种情况下我应该使用哪种设计模式
- 为什么在这种情况下我需要 .template
- 在这种情况下,使用 string_view 是否会导致不必要的字符串复制?
- 我是否访问了已释放的内存,或者在这种情况下DrMemory报告不正确?
- 在这种情况下,"typename..."意味着什么?
- 为什么在这种情况下 x = 44?
- 在这种情况下是私有的吗?试图使操作员<<过载
- 在这种情况下,如何防止C++输出/控制台窗体关闭
- 为什么 lambda nullptr 取消引用在这种情况下有效?
- 为什么在这种情况下,前向声明不起作用?
- 在这种情况下,我应该避免"goto"吗?
- 在这种情况下我可以使用goto吗
- 在这种情况下用goto不好吗?