表面上等效的表达式 (class)A a; 和 (class)A a{}; 之间的区别

Difference between apparently equivalent expression (class)A a;, and (class)A a{};

我想知道,两者之间可能存在的内在差异是什么? A a;A a{};(class一个,没有构造函数)?

我只是在玩代码,发现了一些非常有趣的东西,其中将全局 class 实例声明为 A a; 与使用 A a{}; 相比产生了不同的结果。

这很有趣,因为我了解到:

所以,基本上是一样的东西,但它们确实在特定条件下产生了不同的结果,尽管它是通过打破 ODR 在可能的 UB 领域中发现的。

我的问题是,如果 A a; 严格等同于 A a{};,程序如何产生不同的结果(即使它是错误的代码)?

从逻辑上讲,它们的内部实现之间应该存在一些差异以产生不同的结果,而其他一切都保持完全相同。我想知道有什么不同。

我正在使用 Visual studio 2017.

//extern_src.cpp

class A {
public:
    int a=24;
};

A extern_a; // << here, if i put {} at the end, different results comes out;

//src.cpp

#include <iostream>
class A {
public:
    int a=3;
};

int main() {
    extern A extern_a;
    A a;
    std::cout << a.a << " " << extern_a.a << std::endl;
    return 0;
}

没有{},代码打印出3 3。 所以对于aextern_a,都会调用在src.cpp中定义的classA的构造函数。

使用{},代码打印出3 24。在这种情况下,为 extern_A.

调用 extern_src.cpp 中定义的构造函数

默认构造函数似乎并没有立即被调用,而是在第一种情况的编译过程中稍后调用(class 没有 {} 的实例声明)。

我明白这是因为对单个 class 有多个定义而破坏了 ODR。但我的问题是,如果 A a; 完全等同于 A a{};,因为它们都在调用默认构造函数,那么它们怎么会产生不同的结果呢?

您已经证明您的程序违反了单一定义规则。每 [basic.def.odr]/12:

There can be more than one definition of a class type [...] Given such an entity named D defined in more than one translation unit, then

  • each definition of D shall consist of the same sequence of tokens; and

  • [...]

[...] If the definitions of D do not satisfy these requirements, then the behavior is undefined.

您对 A 的定义不包含相同的标记序列,因此行为未定义。当行为未定义时,所有赌注都将取消。即使是同一个程序在再次编译时也会有不同的行为。并不是说A a;A a{};是不同的语句s(或者更准确地说,simple-declarations ,但不是表达式)。每 [defns.undefined]:

undefined behavior

behavior for which this document imposes no requirements

[ Note: Undefined behavior may be expected when this document omits any explicit definition of behavior or when a program uses an erroneous construct or erroneous data. Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message). Many erroneous program constructs do not engender undefined behavior; they are required to be diagnosed. Evaluation of a constant expression never exhibits behavior explicitly specified as undefined in [intro] through [cpp] of this document ([expr.const]). — end note ]

另见 Undefined, unspecified and implementation-defined behavior to get an idea of the possible consequences of undefined behavior. In particular, one version of GCC (1.17) tried to start the games NetHack, Rogue, and Towers of Hanoi when encountering certain kinds of undefined behavior. [1]