【问题标题】:Why doesn't this "undefined extern variable" result in a linker error in C++17?为什么这个“未定义的外部变量”不会导致 C++17 中的链接器错误?
【发布时间】:2017-07-11 07:21:18
【问题描述】:

我已经在 C++17 编译器 (Coliru) 中编译并运行了以下程序。在程序中,我声明了一个extern 变量,但没有定义它。但是,编译器不会给出链接器错误。

#include <iostream>

extern int i; // Only declaration

int func() 
{
    if constexpr (true)
        return 0;
    else if (i)
        return i;
    else
        return -1;
}

int main() 
{
    int ret = func();
    std::cout<<"Ret : "<<ret<<std::endl;
}

为什么编译器不给出链接器错误?

【问题讨论】:

    标签: c++ linker-errors extern c++17 if-constexpr


    【解决方案1】:

    因为变量没有被 odr 使用。你有一个constexpr if,它总是丢弃可以使用它的分支。

    constexpr if 的要点之一是丢弃的分支甚至不需要编译,只需格式良好。这就是我们可以在废弃分支中调用不存在的成员函数的方法。

    【讨论】:

    • 如果我将constexpr if 更改为普通的if,那么我也不会收到链接器错误。为什么?
    • @Rakete1111 - 我认为是积极优化和非标准。优化器别无选择只能引用变量ideone.com/NjEqoy时不链接
    • @Rakete1111 - 实际上 clang 和 GCC 都为我接受了它。我被难住了。
    • @Rakete1111 - AnT 在他们的回答中有引用。无需诊断。
    • 我认为 GCC 和 clang 将外部声明视为弱定义,以兼容旧的非标准代码。
    【解决方案2】:

    在您的情况下,该变量仅用于丢弃的语句。然而,即使我们忽略了这一事实,C++ 语言规范仍然明确声明不需要诊断缺少定义

    3.2 单一定义规则

    4 每个程序都应包含一个定义,即在该程序中被丢弃的语句之外的每个非内联函数或变量(6.4.1); 无需诊断。

    语言规范理解优化编译器可能足够聪明,可以消除变量的所有 odr-uses。在这种情况下,要求实现检测和报告潜在的 ODR 违规行为是过度且不必要的。

    【讨论】:

    • 即使不需要诊断,是否仍然意味着程序格式错误,因此不应通过编译和链接?
    • @Rick Astley:“格式错误”绝不意味着“不应该通过编译和链接”。只要实现在语言规范需要诊断的地方发布诊断,就可以“成功”编译任何东西。事实上,在 C++ 中真正终止编译所需的唯一特性是#error 指令。
    • @AnT: 几乎是正确的,除了编译器在#error 之后甚至不必停止处理,仅需要发出诊断,程序是格式错误(而#warning 不会使程序格式错误)。见§16.5 [N4140, cpp.error]。 (更新:#warning 不是标准的一部分。)
    • 还有static_assert,没关系。
    • @Deduplicator static_assert(!"do not format harddrive"); 可以生成格式化硬盘的可执行文件。它还必须生成诊断,但该标准对编译器对格式错误的程序所做的操作施加了零限制。这并不重要,因为只有恶意编译器可能会这样做。
    【解决方案3】:

    因为编译器会产生编译器错误,链接器会产生链接器错误...

    不,说真的:

    if constexpr (true)
    

    总是正确的,因此编译器会忽略 if 子句的其余部分,因为它永远不会到达。所以i实际上从未使用过。

    【讨论】:

      【解决方案4】:

      这已经被回答了,但是如果你有兴趣,cppreference.com 正好有这个 constexpr if 的例子:

      constexpr 如果

      以if constexpr 开头的语句称为constexpr if 语句。

      在 constexpr if 语句中,condition 的值必须是根据上下文转换的 bool 类型的 constant expression。如果值为 true,则丢弃 statement-false(如果存在),否则丢弃 statement-true。
      [...]
      丢弃的语句可以odr-use一个未定义的变量:

      extern int x; // no definition of x required
      int f() {
      if constexpr (true)
          return 0;
      else if (x)
          return x;
      else
          return -x;
      }
      

      【讨论】:

        猜你喜欢
        • 2021-04-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-04
        • 2013-07-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多