【问题标题】:C++ assert: the precedence of the expression in an assert macroC++ 断言:断言宏中表达式的优先级
【发布时间】:2014-07-17 01:59:35
【问题描述】:

在 C++ 中:

  assert(  std::is_same<int , int>::value  ); // does not compile

  assert( (std::is_same<int , int>::value) ); // compiles

谁能解释一下原因?

【问题讨论】:

  • 宏忽略编译器语义(当宏展开时这些语义还不存在)。您几乎可以传递任何用逗号分隔的内容。
  • 值得注意的是,这些括号使四个Boost PP data types成为可能。
  • std::is_same 是一个编译时检查,所以无论如何做一个运行时断言没有多大意义!改为执行编译时断言。在 C++11 中有 static_assert ,否则还有 a lot of options
  • @MattMcNabb 这是您不应该做出的假设,因为我相信我有能力编写任何符合 c++ 标准规范的内容。也许我打算延迟运行时的断言,尽管它在编译时是可确定的。也许我公司有一个签到验证系统,它只允许你提交可以编译的代码,我现在只想回家,所以我选择使用运行时断言来通过签到系统。
  • @igbgotiz:在这种情况下,您是在有意地检查损坏的代码。我会解雇你的。 (损坏的代码不是断言也不是 is_same,损坏的代码是导致它被触发的任何原因)

标签: c++ c-preprocessor assert operator-precedence


【解决方案1】:

assert 是一个预处理器宏。预处理器宏是愚蠢的;他们不懂模板。预处理器在括号内看到 10 个标记:

assert( std :: is_same < int , int > :: value );

它以逗号分隔。它不知道这是错误的拆分位置,因为它不明白std::is_same&lt;intint&gt;::value 不是有效的C++ 表达式。

预处理器 足够聪明,不会在多个参数之间分解内部括号对的内容。这就是为什么添加额外的括号可以解决问题。

【讨论】:

  • 这里还有一个static_assert 就足够了:static_assert( std::is_same&lt;int,int&gt;::value, "oops" );
  • 不仅仅是模板。大括号和方括号都不能保护逗号。括号和引号可以。
  • 这不太合理。预处理器显然知道“断言”只需要一个参数,这就是它能够首先报告错误的原因。那么为什么预处理器要进行任何拆分呢?如果 assert 是一个需要两个或更多参数的宏,我可以很容易地理解拆分的尝试。但断言,正如你、我和预审者都知道的那样,只接受一个论点。因此,预处理器为什么会在逗号处尝试拆分并告诉我我给出了太多参数,这超出了我的理解。
  • @igbgotiz 如果预处理器没有在逗号处拆分,那将是一个特殊情况:如果只需要一个参数,则忽略逗号,不要忽略它们如果有多个。我不能代表 C 的设计者,但我认为一般情况下最好避免这种特殊情况。
  • @igbgotiz 这不一样。 程序 只做你告诉它做的事情。如果您需要编写具有基本情况和递归情况的函数,那没有什么问题;代码中清楚地说明了它。 programmer 不应该像 program 总是不断地进入下一行代码一样查阅标准。应避免编程语言语义中的特殊情况,以便程序员可以期望语言功能以最明显的方式工作——即使它很愚蠢。
【解决方案2】:

逗号被视为宏的参数分隔符,但第二种情况下的括号保护参数。我们可以通过查看草案 C++ 标准部分 16.3 宏替换 看到这一点(emphasis mine):

以最外层为界的预处理标记序列 匹配的括号形成类函数的参数列表 宏。列表中的各个参数用逗号分隔 预处理标记,但匹配之间的逗号预处理标记 内括号不分隔参数。 如果有 预处理参数列表中的标记,否则 充当预处理指令,154 行为未定义

通过转到2.2翻译阶段部分,我们可以看到宏扩展发生在语义分析之前,并看到第 4 阶段包括:

执行预处理指令,扩展宏调用, 并且 [...] 然后删除所有预处理指令。

第 7 阶段包括:

[...]每个预处理标记都转换为一个标记。 (2.7)。这 对生成的标记进行语法和语义分析,并 翻译为翻译单元[...]

作为旁注,我们可以看到 Boost 包含一个特殊的宏来处理这种情况: BOOST_PP_COMMA:

BOOST_PP_COMMA 宏扩展为逗号。

然后说:

预处理器将逗号解释为宏调用中的参数分隔符。因此,逗号需要特殊处理。

还有一个例子:

BOOST_PP_IF(1, BOOST_PP_COMMA, BOOST_PP_EMPTY)() // expands to ,

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-05
    • 1970-01-01
    • 1970-01-01
    • 2017-02-18
    • 2016-06-05
    • 2015-10-11
    相关资源
    最近更新 更多