我在这里添加一个答案,以澄清格式错误和未定义行为之间的区别。
[intro.compliance]/p1:
可诊断规则集由本国际标准中的所有句法和语义规则组成,但那些包含“不需要诊断”的明确表示法或被描述为导致“未定义行为”的规则除外。</p>
[defns.ill.formed]:
格式不正确的程序
[defns.well.formed]
根据语法规则、可诊断语义规则和单一定义规则 (3.2) 构建的 C++ 程序。
用英语:一个格式错误的程序应该有一个与之相关的诊断。未定义的行为可以做任何事情:
- 它可以按照您的意图编译和执行。
- 它可以发出诊断。
- 它可以删除您编写的代码。
- 它可以重新格式化最近的磁盘。
(除了第 4 次以外的所有情况在实践中都经常发生)
未定义的行为非常糟糕,恕我直言,C 和 C++ 标准过于宽松地应用了该规范。
从技术上讲,违反Requires子句会导致未定义的行为。
[res.on.required]/p1:
违反函数的 Requires: 段落中指定的先决条件会导致未定义的行为,除非函数的 Throws: 段落指定在违反先决条件时引发异常。
如 MSN 所述,allocator_type::value_type
应与container::value_type
表 99 - 分配器感知容器要求中所述相同。
allocator_type A Requires: allocator_type::value_type
is the same as X::value_type.
(X
表示一个可感知分配器的容器类,其中一个使用类型value_type
为 的分配器)T
A
所以违规如:
std::list<int, std::allocator<long> >
是未定义的行为。所以:
- 可以按照您的意图编译和执行。
- 可以发出诊断。
- 可以删除你写的代码。
- 可以重新格式化最近的磁盘。
就在最近(在我写这篇文章的几周内),libc++(http://libcxx.llvm.org)已经开始诊断这种未定义的行为,static_assert
以便您尽快得到坏消息。
我们决定朝这个方向发展,而不是允许这种行为,因为容器没有设置为允许密切相关的类型之间的转换。例如:
std::list<int, std::allocator<long>> list1;
std::list<int> list2 = list1; // is specified to not work
即,如果您开始将list1
andlist2
视为等效类型,因为无论如何std::allocator
都会得到rebind
'd,那么您会感到失望,因为您发现这两个列表确实是不同的类型,并且无论如何都不是为了互操作而设计的。所以最好尽快得到坏消息,而不是在 2 个月或 2 年后,当您尝试将它们用作等效类型时才发现。
也许未来的标准会将list1
和list2
视为等效类型。这在技术上大多是可行的(std::is_same
可能行不通)。但是我没有听说过这方面的建议。这个方向在我看来不太可能。并且static_assert
,错误很容易诊断。相反,我希望看到标准朝着使此代码格式错误而不是未定义的方向发展。这样做最困难的部分是对标准进行文字锻造,而不是在 std::lib 实现中。