C ++中的抛出和三元运算符

Posted

技术标签:

【中文标题】C ++中的抛出和三元运算符【英文标题】:Throw and ternary operator in C++ 【发布时间】:2011-12-18 22:50:57 【问题描述】:

以下代码使用 G++ 4.6.1 编译,但不适用于 Visual Studio 2008

return (m_something == 0) ? 
    throw std::logic_error("Something wrong happened") : m_something;

事实上,Visual Studio 编译器执行了内部崩溃。

我想知道这是否是标准 C++,为什么它不能用 Visual Studio 编译,但用 G++ 编译?

【问题讨论】:

无论这是否是有效代码,根据标准,当编译器崩溃并出现内部编译器错误(这是 VC 所做的,IIUC)时,这就是编译器中的错误。即使是错误代码,编译器也应该发出有意义的消息,而不是崩溃报告。 记住这是条件运算符,恰好是a三元运算符。 是的,当规范中没有“三元”时感到困惑。 在一段短暂的时期内(在 c++11 和 c++14 之间),从三元抛出是编写可能在编译时失败的 constexpr 函数的唯一方法。 【参考方案1】:

它是标准的 C++。条件表达式中的 then/else 表达式中的任何一个(或两个)都可以作为 throw 表达式(C++98 5.16/2)。

如果 Visual Studio 在编译时崩溃……那似乎很不幸!

【讨论】:

§5.16/2 允许这两个表达式都是 C++11 中的 throw 表达式。 @Mat:哎呀,你说的很对,这里的文字没有改变,第二种选择也允许在 C++98 中抛出。【参考方案2】:

Comeau 编译它没有错误(这是我的最小可编译测试用例):

int main(void)

    int x = 17;
    return x ? throw "Something wrong happened" : 5;

这是标准允许的很好的证据。 MSVC 崩溃的事实也是如此,而不是完全失败并出现错误。

此外,它似乎已在 VC++ 2010 中修复

R:\>cl ternarythrowtest.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

ternarythrowtest.cpp
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:ternarythrowtest.exe
ternarythrowtest.obj

和 x64 版本:

R:\>cl ternarythrowtest.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

ternarythrowtest.cpp
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:ternarythrowtest.exe
ternarythrowtest.obj

如果可能的话,升级你的编译器,这远不是 2010 年修复的唯一错误。

【讨论】:

确实,这可能是因为符合 C++11,请参阅@MooingDuck 的回答。 @AndréCaron:这无法解释“内部编译器错误”。看看约翰的回答,它在 C++98 中也是允许的。 我在@fmorency 的团队中,我是遇到Microsoft Compiler 崩溃的人,他的throw-in-a-ternary-operator 表达式。我们试图弄清楚它是否是标准的,而不是这是否是 Microsoft 编译器中的错误(显然存在 a 错误,因为它崩溃了)。 @AndréCaron:嗯,它是 C++98,而不是 C++11 合规性问题,所以它肯定可以工作。 确实:-)。不幸的是,我们目前坚持使用 Qt 4.7,并且我们支持 Windows,所以我们坚持使用 VS2008 的编译器。【参考方案3】:

来自 C++11 二月草案

§ 5.16/2 如果第二个或第三个操作数的类型(可能是 cv 限定的)void,则左值到右值 (4.1)、数组到指针 (4.2) 和函数到-指针 (4.3) 标准转换是在第二个和第三个操作数上执行的,并且应满足以下条件之一: — 第二个或第三个操作数(但不是两者)是 throw 表达式(15.1);结果是另一个的类型并且是prvalue。 — 第二个和第三个操作数都有 void 类型;结果是 void 类型并且是纯右值。 [ 注意:这包括两个操作数都是 throw 表达式的情况。 ——尾注]

看来throw 算作对void 的评估,这是允许的。

【讨论】:

C++11 与 VS2008 几乎没有关系(除了这不是 C++11 规则,自 C++98 以来它没有变化,请参阅约翰的回答)。 我没有要引用的 C++03 草案(用于 VS2008)的副本,否则我会引用它。没错,它不一定适用于 VS2008。【参考方案4】:

内部崩溃可以认为是 Visual Studio 的一个错误。编译器不应该因为正在编译的代码而崩溃。

这是一个非常奇怪的三元运算符用法,在 return 之前简单的 if 会是一个更可取的习惯用法:

if(m_something == 0)
    throw std::logic_error("Something wrong happened");

return m_something;

【讨论】:

以上是关于C ++中的抛出和三元运算符的主要内容,如果未能解决你的问题,请参考以下文章

三元运算符关联性

意外结果,Gnu C 中的三元运算符

C ++中的三元运算符奇怪的行为

三元运算符注意事项

带括号的三元表达式在 C 中的函数指针声明中返回函数名称的基本原理

mysql三元运算,上下连表