问题 是`char * p = 0; std :: equal(p,p,p)`根据C ++标准定义良好?


以下是根据C ++标准定义明确的吗?

char* p = 0;
std::equal(p, p, p);

问题是这样的:

标准是否要求这样做 std::equal(begin1, end1, begin2) 以这样的方式实现if begin1 == end1, 然后 begin1 和 begin2 可以是任何指针,甚至是一个不指向有效内存对象的指针?

我认为这是标准的意图,但我未能找到一个明确表达的声明。

我担心的原因是VisualStudio显然试图检查“有效性” begin2 甚至在 begin1 == end1。这与我对标准要求的理解相矛盾。

编辑:这是VS 2012的代码,我认为违反了标准:

template<class _InIt1, class _InIt2> inline
bool equal(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2)
{   // compare [_First1, _Last1) to [First2, ...)
    _DEBUG_RANGE(_First1, _Last1);
    _DEBUG_POINTER(_First2);
    return (_Equal1(_Unchecked(_First1), _Unchecked(_Last1), _First2, _Is_checked(_First2)));
}

template<class _Ty> inline
void _Debug_pointer(const _Ty *_First, _Dbfile_t _File, _Dbline_t _Line)
{   // test iterator for non-singularity, const pointers
    if (_First == 0)
        _DEBUG_ERROR2("invalid null pointer", _File, _Line);
}

12080
2017-10-01 15:57


起源

我认为这是明确定义的。 - chris
@chris我不这么认为。 “迭代器”无效,因此它是未定义的行为。您必须先检查p是否为空。 - Neil Kirk
@NeilKirk:他们不是无效的;只是没有不可饶恕。你仍然可以比较 begin1 同 end1因此,算法定义明确,因为它只会进行比较而不会尝试取消引用任何内容。 - Mike Seymour
@NeilKirk:空指针是一个有效的指针,因此是一个有效的迭代器。它是单数的,而不是可解除引用的,但仍可与另一个空指针相媲美,因此 [null,null) 是一个有效的范围。 - Mike Seymour
@NeilKirk:我不知道你为什么这么说,或者你认为“有效”在这种情况下意味着什么。它是一个奇异的迭代器(如C ++ 11 24.2.1 / 5所述),但它与自身相比较,因此是 到达 从它自己(由24.2.1 / 6定义),因此形成一个有效的(空) 范围 与自身(由24.2.1 / 7定义)。此功能的唯一要求是两个输入范围有效;无论微软怎么想,由两个空指针组成的空范围符合这个要求。 - Mike Seymour


答案:


所以我们有25.2.1 / 1表示:

返回:如果对于[first1,last1]范围内的每个迭代器i,则返回true   遵循相应的条件:* i == *(first2 +(i -   first1)),pred(* i,*(first2 +(i - first1)))!= false。

除此以外,   返回false。

在你的情况下,在[0,0]范围内没有迭代器,因此范围内的“每个”迭代器都通过了测试,但是不应该进行实际的测试(因为在测试的范围内不存在迭代器)。

它对我来说看起来像一个VisualStudio的bug。


9
2017-10-01 16:06



这是写的方式,它可能不是一个错误,而是一个不同的解释。也就是说,检查范围[first,last]意味着存在范围。由于[0,0]没有元素,因此它没有范围,因此是无效输入。 - Zac Howland
@Zac Howland没有其他算法有限制阻止您传入空范围。为什么会有所不同? - Mark B
@ShafikYaghmour与之对应的是通过什么是“有效”的迭代器 不 敞篷车。例如:容器端,即一些矢量 v.end()。通过 std::equal(v.end(), v.end(), p) 有更好的工作。我同意马克对这个问题的回答。这应该有效,而VS则相当迂腐。 - WhozCraig
@ZacHowland:标准非常清楚:没有前提条件,范围是非空的,这里的规范明确要求它返回 false 如果是空的 - Mike Seymour
@ZacHowland:是的,对不起,我写道 false 当我的意思 true。我的观点是,它适用于任何有效的输入范围,包括像这样的空范围。 - Mike Seymour


正如@Zac指出的那样,这个检查是Visual Studio在安全性方面更加迂腐。如果您希望Visual Studio在调试版本中更接近标准,则可以通过设置宏来关闭此行为 _ITERATOR_DEBUG_LEVEL为0


2
2017-10-01 21:24



我不怀疑你说的是真的,我知道VS的传统是不严格遵守标准,但正在改进。但是,默认启用与标准冲突的调试功能是不合适的。我希望他们最终会解决这个问题。 - Kristian Spangsege
@KristianSpangsege我看到你在说什么,但我不会屏住呼吸。我猜他们想拥有更多防黑客软件的愿望超过了这些潜在的烦恼,特别是当他们提供一种机制来关闭它时。 - zdan


通过更新,很​​明显它不违反标准,而是调试检查。如果在发布模式下编译它,则不会运行这些检查,并且该函数与标准的描述匹配。

在调试模式下使用该信息非常有用,因为它可以帮助您找到一些难以发现的错误。


0
2017-10-01 18:18



我不同意。他们检查太多了。这妨碍了完美正确和理智的代码。他们可以轻松地修改他们的调试工具,使其符合标准,并在范围不为空时仍然捕获空指针。我很确定它只是一个bug或者是“thinko”。从代数的角度来看(并且,我相信,从标准的角度来看),将不可引用的指针作为空范围的一部分传递没有任何错误或错误。 - Kristian Spangsege
这只是一个警告: Warning 1 warning C4996: 'std::_Equal1': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators' 和一个调试断言(你可以禁用)。 - Zac Howland
假设我正确地解释了标准,我认为这仍然是错误和不恰当的。 - Kristian Spangsege
该标准并不完全适用于调试版本。调试器会做各种各样的技巧来帮助查找/防止/指出在发布版本(要应用的标准)中追踪很难(如果不是不可能)的潜在陷阱。如果调试断言和警告打扰你那么多,你可以禁用它们。 - Zac Howland
我在跨平台库的上下文中工作,所以我必须要求所有应用程序禁用该检查。对我来说更好的解决方案是添加额外的代码来检查特殊情况,但这是一个令人讨厌的问题,对于读者而言似乎是多余的。这不是一个大问题,而是一个不应该是必要的复杂问题。 - Kristian Spangsege


C ++ 11标准在24.2.1 / 5中声明“[i,i)是一个空范围”。

但是,在24.2.1 / 5中,它首先暗示0必须是奇异值,然后指出“大多数表达式的结果对于奇异值是未定义的”。然后列出未定义行为的异常,但不包括比较。

因此,将单数迭代器与等式进行比较可能没有定义,因此无法对[i,i]进行求值。

这也是由于您的运行时错误发生在名为_Equal1()的函数内部。

我认为标准对此很模糊,我完全不确定它是Visual Studio 2012中的一个错误。

http://cplusplus.github.io/LWG/lwg-unresolved.html 在“1213.有效和奇异迭代器的含义未明确”的章节中,这种混淆也很有趣......


0
2017-10-02 13:01