为什么这个"未定义的外部变量"不会导致C++ 17中的链接器错误?

msc*_*msc 38 c++ linker-errors extern c++17 if-constexpr

我已经在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;
}
Run Code Online (Sandbox Code Playgroud)

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

Sto*_*ica 58

因为变量不是使用odr.你有一个constexpr if总是丢弃可以使用它的分支.

其中一个要点constexpr if是丢弃的分支甚至不需要编译,只需要格式良好.这就是我们如何在一个被丢弃的分支中调用不存在的成员函数.

  • @ Rakete1111 - AnT在答案中有引用.无需诊断. (7认同)
  • @Rakete1111 - 我认为,积极的优化和非标准.当优化器别无选择,只能引用变量http://ideone.com/NjEqoy时,它不会链接 (4认同)
  • 如果我将`constexpr if`更改为正常的`if`,那么我也不会收到链接器错误.为什么? (2认同)

AnT*_*AnT 41

在您的情况下,变量仅用于废弃的语句中.但是,即使我们忽略了这一事实,C++语言规范仍然明确指出缺少定义不需要诊断

3.2单一定义规则

4每个程序应包含在废弃陈述之外的该程序中使用的每个非内联函数或变量的一个定义(6.4.1); 无需诊断.

语言规范理解优化编译器可能足够智能以消除变量的所有odr使用.在这种情况下,要求实施检测和报告潜在的ODR违规将是过分和不必要的.

  • @Rick Astley:"形成不良"从未意味着"不应该通过编译和链接".允许实现"成功"编译绝对任何东西,只要它们发出语言规范要求诊断的诊断.事实上,在C++中实际*终止*编译所需的唯一功能是`#error`指令. (13认同)
  • 即使不需要诊断,它是否仍然意味着程序形成不良,因此不应该通过编译和链接? (3认同)
  • @AnT:几乎是正确的,除了编译器甚至不必在"#error"之后停止处理,它只是**需要发出诊断,并且程序格式不正确(而`#warning`不呈现该计划格式不正确).见§16.5[N4140,cpp.error].(更新:`#warning`不是标准的一部分.) (2认同)

Ren*_*ene 9

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

不,认真:

if constexpr (true)
Run Code Online (Sandbox Code Playgroud)

始终为true,因此编译器会忽略if子句的其余部分,因为它永远不会到达.所以i从来没有使用过.