C++ 在下面的代码中,我是否可以使用任何编译器标志来报告有关UB的警告?

C++ 在下面的代码中,我是否可以使用任何编译器标志来报告有关UB的警告?,c++,gcc,language-lawyer,compiler-optimization,undefined-behavior,C++,Gcc,Language Lawyer,Compiler Optimization,Undefined Behavior,我对GCC编译器不是很熟悉,我正在尝试使用中的两个源文件运行代码。让我们从使用主文件(默认)的第一个代码和名为other.cc的第二个源文件开始,如下所示: 主文件 #include <iostream> struct A { int i = 1; static const int k = 1; }; extern A a; // The object a is defined in other.cc int main() { std::cout &l

我对GCC编译器不是很熟悉,我正在尝试使用中的两个源文件运行代码。让我们从使用主文件(默认)的第一个代码和名为
other.cc的第二个源文件开始,如下所示:

主文件

#include <iostream>
struct A {
    int i = 1;
    static const int k = 1;
};
extern A a;    // The object a is defined in other.cc

int main() {
    std::cout << a.i << '\n';
    std::cout << a.k << '\n';
}
请注意,我必须将文件名
other.cc
插入到对话框左侧的“编译器选项”框中,以便编译该文件并链接到最终的目标文件中。运行这段代码,我得到下面打印的数字2和1,它们是正确的

2
1
现在,如果我从编译和链接过程中排除文件
other.cc
,通过从编译选项框中删除其名称,我会得到一个链接错误,关于表达式中使用的非静态数据成员
a::I
的引用

std::cout << a.i << '\n';

std::cout编译器可能没有使其失败的选项,因为编译器只检查语言,而语言允许这样做

链接器将有一个使其失败的选项,您可以通过大多数编译器将选项传递给链接器,包括
g++
。我可以通过以下方式使gcc失败:

g++ -std=c++11 -Xlinker --require-defined=a main.cpp
这就产生了错误

/usr/bin/ld: required symbol `a' not defined
collect2: error: ld returned 1 exit status
您没有要求这样做,但是
clang++
更容易一些:

clang++ -std=c++11 -u a main.cpp 
还有更详细的内容:

Undefined symbols for architecture x86_64:
  "a", referenced from:
     -u command line option
     (maybe you meant: __ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m, _main , __ZNSt3__111char_traitsIcE3eofEv , __ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_ , __ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_c , ___clang_call_terminate , __ZNSt3__111char_traitsIcE11eq_int_typeEii )
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

试一试。

编译器可能没有让它失败的选项,因为编译器只检查语言,而语言允许这样做

链接器将有一个使其失败的选项,您可以通过大多数编译器将选项传递给链接器,包括
g++
。我可以通过以下方式使gcc失败:

g++ -std=c++11 -Xlinker --require-defined=a main.cpp
这就产生了错误

/usr/bin/ld: required symbol `a' not defined
collect2: error: ld returned 1 exit status
您没有要求这样做,但是
clang++
更容易一些:

clang++ -std=c++11 -u a main.cpp 
还有更详细的内容:

Undefined symbols for architecture x86_64:
  "a", referenced from:
     -u command line option
     (maybe you meant: __ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m, _main , __ZNSt3__111char_traitsIcE3eofEv , __ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_ , __ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_c , ___clang_call_terminate , __ZNSt3__111char_traitsIcE11eq_int_typeEii )
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

试一试。

我想另一个问题的答案会更有启发性。对未定义行为的恐惧在于,当使用不同的编译器时,无法保证相同的运行时结果。因此:

我不需要错误消息来确保运行时行为不依赖于所使用的编译器吗? 不,有两种情况需要考虑。< /P> 第一种情况:编译器用值
1
替换
a.k
。运行时行为是预期的结果

第二种情况:编译器不会用固定值替换
a.k
,而是生成一些引用
a
变量的机器代码。(这是您试图使用编译器选项触发的内容。)在这种情况下,链接器会看到引用,并且在任何转换单元中找不到变量时会发出错误。您不能前进到运行时行为,因此不会得到与第一种情况不一致的运行时结果

因此,理论上,在切换编译器时可能会有一些麻烦,但不包括典型的未定义行为的模糊错误

如果有些人不认为这个答案有启发性,我现在回到原来的问题,重新措辞一点

当发生这种违反一个定义规则的情况时,是否有任何标志可用于强制显示诊断消息(错误或警告)?
取决于编译器,但可能不是。请注意,[basic.def.odr]/10提到“无需诊断”,因此在发生此特定冲突时,编译器不需要提供诊断消息。为什么不呢?可能是因为上述原因:要么在稍后阶段(即链接)触发消息,要么预期行为将通过。在这种情况下,通过要求错误/警告来增加编译器的簿记负担是没有好处的。

我认为另一个问题的答案会更有启发性。对未定义行为的恐惧在于,当使用不同的编译器时,无法保证相同的运行时结果。因此:

我不需要错误消息来确保运行时行为不依赖于所使用的编译器吗? 不,有两种情况需要考虑。< /P> 第一种情况:编译器用值
1
替换
a.k
。运行时行为是预期的结果

第二种情况:编译器不会用固定值替换
a.k
,而是生成一些引用
a
变量的机器代码。(这是您试图使用编译器选项触发的内容。)在这种情况下,链接器会看到引用,并且在任何转换单元中找不到变量时会发出错误。您不能前进到运行时行为,因此不会得到与第一种情况不一致的运行时结果

因此,理论上,在切换编译器时可能会有一些麻烦,但不包括典型的未定义行为的模糊错误

如果有些人不认为这个答案有启发性,我现在回到原来的问题,重新措辞一点

当发生这种违反一个定义规则的情况时,是否有任何标志可用于强制显示诊断消息(错误或警告)?
取决于编译器,但可能不是。请注意,[basic.def.odr]/10提到“无需诊断”,因此在发生此特定冲突时,编译器不需要提供诊断消息。为什么不呢?可能是因为上述原因:要么在稍后阶段(即链接)触发消息,要么预期行为将通过。在这种情况下,通过要求错误/警告来增加编译器的记账负担是没有益处的。

@ CyykRAMER:聚合初始化。“未定义行为”是由C++标准定义的术语,用来描述一类不正确的C++程序。它(通常)与您用来处理程序的编译器、编译器标志等无关。@zipzit-“UB”