天道酬勤,学无止境

为什么 ControlCollection 不抛出 InvalidOperationException?(Why does ControlCollection NOT throw InvalidOperationException?)

问题

按照这个问题 Foreach 循环处理跳过迭代的控件,它让我觉得在不断变化的集合上允许迭代:

例如,以下内容:

List<Control> items = new List<Control>
{
    new TextBox {Text = "A", Top = 10},
    new TextBox {Text = "B", Top = 20},
    new TextBox {Text = "C", Top = 30},
    new TextBox {Text = "D", Top = 40},
};

foreach (var item in items)
{
    items.Remove(item);
}

投掷

InvalidOperationException:集合被修改; 枚举操作可能无法执行。

但是,在 .Net 表单中,您可以执行以下操作:

this.Controls.Add(new TextBox {Text = "A", Top = 10});
this.Controls.Add(new TextBox {Text = "B", Top = 30});
this.Controls.Add(new TextBox {Text = "C", Top = 50});
this.Controls.Add(new TextBox {Text = "D", Top = 70});

foreach (Control control in this.Controls)
{
    control.Dispose();
}

它跳过元素,因为迭代器运行在一个不断变化的集合上,而不抛出异常

漏洞? 如果底层集合发生变化,迭代器是否需要抛出InvalidOperationException

所以我的问题是为什么在不断变化的ControlCollection上迭代不会抛出 InvalidOperationException?

附录:

IEnumerator 的文档说:

枚举器没有对集合的独占访问权; 因此,通过集合进行枚举本质上不是线程安全的过程。 即使在同步集合时,其他线程仍然可以修改集合,这会导致枚举器抛出异常

回答1

答案可以在 ControlCollectionEnumerator 的参考源中找到

private class ControlCollectionEnumerator : IEnumerator {
    private ControlCollection controls; 
    private int current;
    private int originalCount;

    public ControlCollectionEnumerator(ControlCollection controls) {
        this.controls = controls;
        this.originalCount = controls.Count;
        current = -1;
    }

    public bool MoveNext() {
        // VSWhidbey 448276
        // We have to use Controls.Count here because someone could have deleted 
        // an item from the array. 
        //
        // this can happen if someone does:
        //     foreach (Control c in Controls) { c.Dispose(); }
        // 
        // We also dont want to iterate past the original size of the collection
        //
        // this can happen if someone does
        //     foreach (Control c in Controls) { c.Controls.Add(new Label()); }

        if (current < controls.Count - 1 && current < originalCount - 1) {
            current++;
            return true;
        }
        else {
            return false;
        }
    }

    public void Reset() {
        current = -1;
    }

    public object Current {
        get {
            if (current == -1) {
                return null;
            }
            else {
                return controls[current];
            }
        }
    }
}

请特别注意MoveNext()中明确解决此问题的注释。

IMO 这是一个被误导的“修复”,因为它通过引入一个微妙的错误来掩盖一个明显的错误(如 OP 所指出的,元素被悄悄地跳过)。

回答2

在 foreach 控件 c# skipping 控件的注释中提出了同样的抛出异常问题。 该问题使用类似的代码,除了在调用Dispose()之前从Controls明确删除了子Control ...

foreach (Control cntrl in Controls)
{
    if (cntrl.GetType() == typeof(Button))
    {
        Controls.Remove(cntrl);
        cntrl.Dispose();
    }
}

我能够仅通过文档找到对这种行为的解释。 基本上,在枚举时修改任何集合总是会导致抛出异常是一个错误的假设; 这种修改会导致未定义的行为,如果有的话,取决于特定的集合类如何处理这种情况。

根据 IEnumerable.GetEnumerator() 和 IEnumerable<>.GetEnumerator() 方法的说明...

如果对集合进行了更改,例如添加、修改或删除元素,则枚举器的行为是未定义的。

诸如 Dictionary<>、List<> 和 Queue<> 之类的类被记录为在枚举期间修改时抛出 InvalidOperationException...

只要集合保持不变,枚举器就保持有效。 如果对集合进行了更改,例如添加、修改或删除元素,则枚举数将不可恢复地失效,并且下一次对 MoveNext 或 IEnumerator.Reset 的调用将引发 InvalidOperationException。

值得注意的是,正是我上面提到的每个类,而不是它们都实现的接口,通过InvalidOperationException指定了显式失败的行为。 因此,它是否因异常而失败取决于每个类。

较旧的集合类(例如 ArrayList 和 Hashtable)专门将这种情况下的行为定义为未定义的枚举数无效...

只要集合保持不变,枚举器就保持有效。 如果对集合进行了更改,例如添加、修改或删除元素,则枚举数将不可恢复地失效并且其行为未定义。

...虽然在测试中我发现这两个类的枚举器实际上在失效后会抛出InvalidOperationException

与上面的类不同,Control.ControlCollection 类既没有定义也没有评论这种行为,因此上面的代码“仅仅”以一种微妙的、不可预测的方式失败,没有例外地明确指示失败; 它从未说过它会明确失败。

因此,一般来说,在枚举期间修改集合肯定会(可能)失败,但保证会引发异常。

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • Why does ControlCollection NOT throw InvalidOperationException?
    Following this question Foreach loop for disposing controls skipping iterations it bugged me that iteration was allowed over a changing collection: For example, the following: List<Control> items = new List<Control> { new TextBox {Text = "A", Top = 10}, new TextBox {Text = "B", Top = 20}, new TextBox {Text = "C", Top = 30}, new TextBox {Text = "D", Top = 40}, }; foreach (var item in items) { items.Remove(item); } throws InvalidOperationException: Collection was modified; enumeration operation may not execute. However in a .Net Form you can do: this.Controls.Add(new TextBox {Text = "A", Top =
  • 为什么 std::stof 在传递无法转换的参数时不抛出?(Why does std::stof not throw when passed an argument it cannot convert?)
    问题 我正在处理一个项目,我想在其中接受{float}{single-letter-identifier}形式的输入,例如15.6E或10W 。 为此,我认为我可以获取输入字符串, std::stof最后一个字母,然后检查是否可以使用std::stof执行转换为浮点数。 这将嵌套在try-catch块中,并允许我通知用户输入无效。 此处 STL 的开放标准(第 653 页)指出std::stof抛出: invalid_argument如果 wcstod 或 wcstold 报告无法执行转换。 但是,它在传递无法转换的内容时不会抛出,例如"48East" 。 重现此行为的代码示例如下: std::wstring szString = L"48East"; try{ float f = std::stof(szString); } catch( ... ) { std::cout << "test" << std::endl; } 这是在 MSVC10 上使用/Od在调试模式下编译的,所以我假设调用没有被优化掉。 我很感激任何帮助(或关于我误解/误读规范的地方的指导!)。 回答1 当我阅读它时, stof尽可能多地转换输入字符串,直到找到无法转换的内容。 如果它不能转换任何东西,它会抛出invalid_argument 。 回答2 MSVC 实现从头开始解析有效字符并成功转换它们
  • 当项目在枚举时发生变化会影响枚举吗?(When items change while being enumerated does it affects the enumeration?)
    问题 想象一下,在一个 foreach(var item in enumerable) 可枚举项发生变化。 会影响当前的foreach吗? 例子: var enumerable = new List<int>(); enumerable.Add(1); Parallel.ForEach<int>(enumerable, item => { enumerable.Add(item + 1); }); 它会永远循环吗? 回答1 通常,它应该抛出异常。 GetEnumerator() 的List<T>实现提供了一个Enumerator<T>对象,其MoveNext()方法如下所示(来自 Reflector): public bool MoveNext() { List<T> list = this.list; if ((this.version == list._version) && (this.index < list._size)) { this.current = list._items[this.index]; this.index++; return true; } return this.MoveNextRare(); } private bool MoveNextRare() { if (this.version != this.list._version) {
  • 为什么asInstanceOf不抛出ClassCastException?(Why asInstanceOf doesn't throw a ClassCastException?)
    问题 为什么asInstanceOf不抛出ClassCastException? scala> List("a").asInstanceOf[List[Int]] res34: List[Int] = List(a) 回答1 从scaladoc: 请注意,在运行时进行转换的成功是Scala擦除语义的模数。 因此,表达式1.asInstanceOf [String]在运行时将引发ClassCastException,而表达式List(1).asInstanceOf [List [String]]则不会。 在后一个示例中,由于将type参数作为编译的一部分进行了擦除,因此无法检查列表的内容是否为请求的类型。 如果尝试访问变量或映射转换,则其正确性将引发异常: scala> List("a").asInstanceOf[List[Int]] res0: List[Int] = List(a) scala> res0 res1: List[Int] = List(a) scala> res0(0) java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer List("a").map(_.asInstanceOf[Int]) java.lang.ClassCastException
  • 为什么 std::shared_ptr 解引用不抛出空指针异常(或类似异常)?(Why doesn't std::shared_ptr dereference throw a null pointer exception (or similar)?)
    问题 异常是 C++ 的重要组成部分,使用它的原因之一(我知道有很多更重要的其他原因)是为了避免使用大量if语句混淆代码的不必要检查(也许这是一个不正确的假设? )。 所以现在我很好奇为什么std::shared_ptr::operator*和std::shared_ptr::operator->不抛出null_ptr_exception或类似的? 回答1 我的理解是智能指针类被设计成看起来和行为像原始指针。 鉴于此指导性设计原则,理想情况下,遗留代码可以使用等效的所有权语义简单地将原始指针的使用替换为智能指针,并且代码将完全像以前一样工作。 因此,更改取消引用智能指针的行为不应进行任何额外的检查或抛出异常(即,因为原始指针不会以这种方式运行)。 向标准添加智能指针的提议表明了这一设计决策(将通用智能指针添加到库技术报告的提议): 三、 设计决策 A. 一般原则 “尽可能接近原始指针,但不能更接近” 回答2 如果每次取消引用共享指针都需要检查nullptr并有条件地抛出异常,则可能会有大量冗余检查、代码膨胀和开销。 当然 - 优化器可能会消除其中的一些,但仍然......相反,程序员希望在许多取消引用之前检查一次。
  • 为什么 Junit 测试不抛出 javax.persistence.PersistenceException?(Why doesn't Junit test throw javax.persistence.PersistenceException?)
    问题 我运行 JUnit4 测试来测试我的 JPA 存储库。 但是当我尝试运行这个必须抛出javax.persistence.PersistenceException测试时,我的测试失败了 - 它没有得到所需的异常。 我已经在 H2、HSQL、Derby 和 MySQL 上试过了。 这里是: @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:testContext.xml" }) @Transactional @FixMethodOrder(MethodSorters.JVM) public class RepositoryTestTemplate extends AbstractTransactionalJUnit4SpringContextTests { } public class AccountRepositoryTest extends RepositoryTestTemplate { @Autowired AccountRepository accountRepository; @Test(expected = PersistenceException.class) public void doubleSaveMethodCallTest() {
  • 为什么这段代码不抛出NullPointerException(why doesn't this code throw NullPointerException)
    问题 我只是在和朋友讨论使用类名调用静态方法的问题,并尝试了这段代码并期望它在运行时抛出NPE。 我只想了解执行顺序。 public class One { public static void method() { System.out.println("in static one"); } } public class Two { static One o; public static void main(String[] args) { o.method(); // expected NPE here, as o is null } } 我知道应该使用它们的类名来调用静态方法,我什至知道当我们使用instance调用静态方法时,IDE会发出编译器警告。 但是我们也可以通过创建实例来调用它们,但是,我从未在此处创建实例, o应该将其默认值设置为null,因此调用o.method()应该在运行时抛出NPE,但事实并非如此。 你们能告诉我们这段代码的执行顺序如何吗? 回答1 之所以起作用,是因为重要的是o字段的编译时类型。 编译器将编译o.method()成相同的字节代码作为One.method() 特别是,如果您有一个扩展了One的类Two ,并且都声明了一个static void method() ,那么 One x = new Two(); x.method(); //
  • 为什么我的 WCF 端点不抛出 Max Clock Skew 异常?(Why doesn't my WCF endpoint throw a Max Clock Skew exception?)
    问题 对于我的应用程序中几乎所有(安全)WCF 服务端点,如果客户端的系统时钟在未来或过去设置得太远,我会从 WCF 时钟偏移机制中得到一个异常(此处描述:http://www.danrigsby .com/blog/index.php/2008/08/26/changed-the-default-clock-skew-in-wcf/)。 然而,实现我的 Login() 方法的一个端点永远不会抛出这个异常,即使它启用了传输安全(自然不需要凭据)。 为什么“时钟偏移机制”不适用于此端点? 也许是因为 clientCredentialType 设置为“无”? 例如,这是我的配置的简化版本: <services> <service name="Foo"> <endpoint address="" binding="wsHttpBinding" bindingConfiguration="binding1" contract="IFoo" /> </service> </services> <bindings> <wsHttpBinding> <binding name="binding1" maxReceivedMessageSize="100000000"> <readerQuotas maxDepth="1000000000" maxArrayLength="1000000000"
  • 为什么我们不抛出这些异常?(Why are we not to throw these exceptions?)
    问题 我碰到了这个MSDN页面,其中指出: 不要故意从您自己的源代码中引发Exception,SystemException,NullReferenceException或IndexOutOfRangeException。 不幸的是,它不会费心解释原因。 我可以猜测出原因,但我希望对此问题更有权威的人可以提供自己的见解。 前两个很明显,但是后两个看起来就像您想雇用的(事实上,我有)。 此外,这些是唯一应避免的例外吗? 如果还有其他人,它们是什么?为什么也要避免它们? 回答1 异常是所有异常的基本类型,因此非常不确定。 您永远不要抛出此异常,因为它根本不包含任何有用的信息。 调用代码捕获异常不会使故意抛出的异常(从您的逻辑)与其他完全不希望的系统异常,并指出真正的错误。 同样的原因也适用于SystemException。 如果查看派生类型的列表,则可以看到大量其他语义非常不同的其他异常。 NullReferenceException和IndexOutOfRangeException是另一种。 现在,这些都是非常具体的例外,因此抛出它们可能很好。 但是,您仍然不希望抛出这些错误,因为它们通常意味着您的逻辑中存在一些实际错误。 例如,null引用异常意味着您正在尝试访问null的对象的成员。 如果这在您的代码中是可能的,那么您应该始终显式检查null并引发更有用的异常
  • 除以0.0时,为什么Java不抛出异常?(Why doesn't Java throw an Exception when dividing by 0.0?)
    问题 我有代码来计算2个数字之间的百分比差- (oldNum - newNum) / oldNum * 100; -两个数字均为double 。 我希望在oldNum为0的情况下必须添加某种检查/异常处理。但是,当我对oldNum和newNum的值均为0.0进行测试运行时,执行继续,就好像什么都没有发生并且没有引发任何错误一样。 如果将这些代码与int一起运行,肯定会导致算术除数为零的异常。 对于double为什么Java会忽略它? 回答1 从数学上讲,零除的结果是undefined ,可以用浮点数/双精度数表示(因为NaN不是数字),但是从根本上讲这不是错误的。 由于整数必须具有特定的数值,因此在处理它们时必须在除以零时引发错误。 回答2 Java的float和double类型,与几乎所有其他语言(以及几乎任何硬件FP单元)一样,实现了针对浮点数学运算的IEEE 754标准,该标准要求除以零以返回特殊的“无穷大”值。 引发异常实际上会违反该标准。 整数算法(由Java以及大多数其他语言和硬件以二进制补码表示)是不同的,并且没有特殊的无穷或NaN值,因此抛出异常是一种有用的行为。 回答3 双精度数的存储方式与int完全不同。 有关Java如何处理双重计算的详细说明,请参见http://firstclassthoughts.co.uk/java/traps/java_double
  • 如果在声明变量之前使用了变量,为什么不抛出ReferenceError?(Why is no ReferenceError being thrown if a variable is used before it’s declared?)
    问题 我试图将注意力投向JavaScript中引发的参考错误的行为。 在以下示例中,第二行引发ReferenceError ,执行中断: var obj = {}; obj.func1 = func2; alert('Completed'); 在此示例中,尽管obj.func1仍未undefined ,但代码已成功完成: var obj = {}; obj.func1 = func2; var func2 = function() { alert('func2'); }; alert('Completed'); 我的假设是在第二行将抛出相同的错误,如果不是这种情况,我希望obj.func1正确引用func2 ,但是我一直是双盲的。 那么,这到底是怎么回事? 回答1 var吊起; 该变量存在于当前范围内。 因此,第二个示例等效于: var obj; var func2; obj = {}; obj.func1 = func2; func2 = function() { alert('func2'); } alert('Completed'); 因此,执行分配时,名称func2是已知的,但undefined 。 在第一个示例中,它是未知的,从而引发ReferenceError 。 回答2 这是由于Javascript变量声明“吊装”引起的。 用var声明的变量在函数中随处可见
  • 为什么当我测试某个方法引发异常而该方法引发异常时,测试是否停止?(Why, when I am testing that a method throws an exception and the method throw an exception, does the test stop?)
    问题 我有一个单元测试,测试如果method时抛出异常condition存在,并且method不抛出异常如预期。 - (void)testMethodThrowsWhenConditionIsPresent { XCTAssertThrows([Foo methodWithCondition: condition], @"Condition is true, method should throw exception"); } 这是异常源: - (void)methodWithCondition:(someType)condition { if (condition) { [NSException raise: @"condition is true!" format: @"condition is true!"]; } } 为什么测试在引发异常的行处停止? 测试没有继续,它在那行停止,当我希望它继续并从XCTAssertThrows()返回1 ,使测试成功。 相反,测试通过Xcode停止,使我进入抛出的行,并带有绿色的“线程1:断点1.1”,调试器出现在控制台中。 回答1 引发执行后,为什么测试会停止? 因为您有一个断点,所以它会停止执行。 为什么在删除断点后引发异常时我的应用程序崩溃? 因为您有未处理的异常。 未处理的异常会导致程序崩溃。 如何处理异常,以免程序崩溃?
  • 为什么“continue”语句不能放在“finally”块中?(Why can't a 'continue' statement be inside a 'finally' block?)
    问题 我没有问题; 我只是好奇。 想象以下场景: foreach (var foo in list) { try { //Some code } catch (Exception) { //Some more code } finally { continue; } } 这不会编译,因为它会引发编译器错误 CS0157: 控制不能离开 finally 子句的主体 为什么? 回答1 无论是否抛出异常, finally块都会运行。 如果抛出异常,那他妈的会continue做什么? 您不能继续执行循环,因为未捕获的异常会将控制转移到另一个函数。 即使没有抛出异常, finally也会在 try/catch 块内的其他控制转移语句运行时运行,例如return ,这带来了同样的问题。 简而言之,对于finally的语义,允许将控制从finally块内部转移到它的外部是没有意义的。 用一些替代语义支持这一点会更令人困惑而不是有帮助,因为有一些简单的解决方法可以使预期的行为方式更清晰。 所以你得到一个错误,并被迫正确思考你的问题。 这是 C# 中普遍存在的“把你扔进成功的坑”的想法。 如果您想忽略异常(通常是一个坏主意)并继续执行循环,请使用 catch all 块: foreach ( var in list ) { try{ //some code }catch{ continue; } }
  • 当两个值都是双倍时,为什么没有 ArithmeticException(除以零)? [复制](why there is no ArithmeticException( divide by zero) when both values are double? [duplicate])
    问题 这个问题在这里已经有了答案: 为什么在除以 0.0 时 Java 不抛出异常? (5 个回答) 7年前关闭。 看过Double.java的源代码和一些常量就像 /** * Constant for the Not-a-Number (NaN) value of the {@code double} type. */ public static final double NaN = 0.0 / 0.0; /** * Constant for the positive infinity value of the {@code double} type. */ public static final double POSITIVE_INFINITY = 1.0 / 0.0; /** * Constant for the negative infinity value of the {@code double} type. */ public static final double NEGATIVE_INFINITY = -1.0 / 0.0; 但我想知道为什么它不抛出 ArithmeticException(除以零)? 我试过了 public static final int VALUE = 0/0; 现在它抛出异常但是当我说 public static final double
  • 为什么Java编译器允许在throws部分中列出该方法无法抛出的异常(Why does the Java compiler allow exceptions to be listed in the throws section that it is impossible for the method to throw)
    问题 如果有一些代码显然不能引发异常,则Java编译器似乎不一致,而您编写的周围代码声明该代码可以引发该异常。 考虑这些代码片段。 片段1 catch从未抛出的异常。 public void g(){ try { } catch (FileNotFoundException e) {//any checked exception } } 是带信息的编译错误 Unreachable catch block for FileNotFoundException. This exception is never thrown from the try statement body 片段2 throws声明,指示从未抛出的异常。 public void g() throws FileNotFoundException{ } 它编译良好。 因此,第一个代码段的结果表明,编译器可以计算方法是否可以抛出throws列表中列出的throws 。 因此,似乎编译器故意不报告第二个片段的错误。 但为什么? 即使编译器知道无法抛出这些异常,为什么编译器仍允许您在throws部分中编写异常? 回答1 编译器允许这样做,因为方法的throws子句是方法签名的一部分,而不是其实现的一部分。 在使签名保持不变的同时,实现可能会在某些时候发生变化。 旧的实现可能引发了检查异常,而新的实现则没有。 否则
  • List 抛出 ConcurrentModificationException 但 set 不抛出 ConcurrentModificationException? [复制](List throws ConcurrentModificationException but set does not throws ConcurrentModificationException? [duplicate])
    问题 这个问题在这里已经有了答案: 为什么抛出 ConcurrentModificationException 以及如何调试它8 个回答 2年前关闭。 我有以下两个java类 import java.util.*; public class ArrayListTest032 { public static void main(String[] ar) { List<String> list = new ArrayList<String>(); list.add("core java"); list.add("php"); list.add("j2ee"); list.add("struts"); list.add("hibernate"); Iterator<String> itr = list.iterator(); while (itr.hasNext()) { System.out.println(itr.next()); } list.remove("php"); while (itr.hasNext()) { System.out.println(itr.next()); } } } 当我运行上面的代码时,我得到下面的输出。 core java php j2ee struts hibernate Exception in thread "main" java.util
  • 为什么 XmlSerializer 会抛出 InvalidOperationException?(Why is XmlSerializer throwing an InvalidOperationException?)
    问题 public void Save() { XmlSerializer Serializer = new XmlSerializer(typeof(DatabaseInformation)); /* A first chance exception of type 'System.IO.FileNotFoundException' occurred in mscorlib.dll A first chance exception of type 'System.IO.FileNotFoundException' occurred in mscorlib.dll A first chance exception of type 'System.InvalidOperationException' occurred in System.Xml.dll */ // .... } 如果您需要,这是整个课程: public class DatabaseInformation { /* Create new database */ public DatabaseInformation(string name) { mName = name; NeedsSaving = true; mFieldsInfo = new List<DatabaseField>(); } /* Read from
  • 对象上的方法调用不会引发异常(Method call on object does NOT throw exception)
    问题 这些天我遇到了许多奇怪的情况:-) 我在 ASP.Net 应用程序中测试了以下两个代码。 当字符串值为空时,代码 1 抛出异常(如预期),而代码 2 不抛出异常(与预期相反)。 为什么“ EVAL ”在这种情况下不抛出异常? EVAL 会一直这样工作(即“无一例外”)还是只是运气? 是否有任何 MSDN 参考说“Eval”返回空字符串? //代码 1 :导致异常 string test = Convert.ToString(subscriber.EncryptedSSN).Substring(0, Convert.ToString(subscriber.EncryptedSSN).Length >= 5 ? 5 : Convert.ToString(subscriber.EncryptedSSN).Length); //代码 2 : 不抛出异常 <%# Convert.ToString(Eval("EncryptedSSN")).Substring(0, Convert.ToString(Eval("EncryptedSSN")).Length >= 5 ? 5 : Convert.ToString(Eval("EncryptedSSN")).Length) %> 参考资料: DBNull 的评估检查不起作用 Convert.ToString 对于“NULL 对象”和
  • 为什么在编译时不强制执行 noexcept ?(Why noexcept is not enforced at compile time?)
    问题 您可能知道 C++11 有 noexcept 关键字。 现在丑陋的部分是这样的: 请注意,函数上的 noexcept 规范不是编译时检查; 它只是程序员通知编译器一个函数是否应该抛出异常的一种方法。 http://en.cppreference.com/w/cpp/language/noexcept_spec 那么这是委员会的设计失败还是他们只是把它作为编译作者的练习:) 从某种意义上说,体面的编译器会强制执行它,坏的编译器仍然可以合规? 顺便说一句,如果你问为什么没有第三个选项(也就是不能完成),原因是我可以很容易地想到一种(慢)方法来检查函数是否可以抛出。 如果您将输入限制为 5 和 7(也就是我保证文件不会包含 5 和 7 之外的任何内容),那么问题当然是不存在的,并且只有在您给它 33 时才会抛出,但恕我直言,这不是一个现实问题。 回答1 委员会非常清楚地考虑了(试图)抛出异常规范所不允许的异常的可能性被认为是格式错误的,并拒绝了这个想法。 根据 15.4/11 美元: 实现不应仅仅因为表达式在执行时抛出或可能抛出包含函数不允许的异常而拒绝表达式。 [ 例子: extern void f() throw(X, Y); void g() throw(X) { f(); // OK } 对f的调用是格式良好的,即使在调用时f可能会抛出g不允许的异常Y —结束示例]
  • 尽管声明为 noexcept(false),但为什么 std::vector 使用移动构造函数(Why does std::vector use the move constructor although declared as noexcept(false))
    问题 无论我在互联网上的何处阅读,强烈建议如果我希望我的类与std::vector一起工作(即std::vector使用我类中的移动语义),我应该将移动构造函数声明为“noexcept” (或noexcept(true) )。 为什么std::vector使用它,即使我将它标记为noexcept(false)作为实验? #include <iostream> #include <vector> using std::cout; struct T { T() { cout <<"T()\n"; } T(const T&) { cout <<"T(const T&)\n"; } T& operator= (const T&) { cout <<"T& operator= (const T&)\n"; return *this; } ~T() { cout << "~T()\n"; } T& operator=(T&&) noexcept(false) { cout <<"T& operator=(T&&)\n"; return *this; } T(T&&) noexcept(false) { cout << "T(T&&)\n"; } }; int main() { std::vector<T> t_vec; t_vec.push_back(T()); } 输出: T() T(T&&