这是未定义的行为还是误报警告?

Is this undefined behavior or a false positive warning?

考虑以下代码:

class A {
private:
  int a;

public:
  A(int a) : a(a) { }
};

class B : public A {
private:
  int b;

  bool init() {
    b = 0;
    return true;
  }

public:
  // init() is a hack to initialize b before A()
  // B() : b(0), A(b) {} yields -Wreorder
  // B() : A((b = 0)) {} no warning (but this one doesn't work so well with non-pod (pointer) types)
  B() : A(init() ? b : 0) {}
};

现在尝试用 clang 编译这段代码...

$ clang++ test.cpp -fsyntax-only
test.cpp:19:20: warning: field 'b' is uninitialized when used here [-Wuninitialized]
B() : A(init() ? b : 0) {}
                 ^
1 warning generated.

GCC 不打印任何警告,即使是 -Wall -Wextra-pedantic

这是未定义的行为。根据 [class.base.init]:

In a non-delegating constructor, initialization proceeds in the following order:
— First, and only for the constructor of the most derived class (1.8), virtual base classes ...
— Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
— Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).

b 不会在 A 基础 class 被初始化时被初始化。由于相同的原因,赋值 b = 0 本身是未定义的行为 - b 在调用时尚未初始化。它的默认构造函数仍然会在 A 的构造函数之后被调用。

如果要确保首先初始化 b,典型的方法是 base-from-member idiom:

struct B_member {
    int b;
    B_member() : b(0) { }
};

class B : public B_member, public A 
{
public:
    B() : A(b)  // B_member gets initialized first, which initializes b
                // then A gets initialized using 'b'. No UB here.
    { };
};

在任何一种情况下,在初始化基 类 之前调用成员函数都会调用未定义的行为。 §12.6.2/16:

Member functions (including virtual member functions, 10.3) can be called for an object under construction. Similarly, an object under construction can be the operand of the typeid operator (5.2.8) or of a dynamic_cast (5.2.7). However, if these operations are performed in a ctor-initializer (or in a function called directly or indirectly from a ctor-initializer) before all the mem-initializers for base classes have completed, the result of the operation is undefined. [ Example:

class A {
public:
  A(int);
};

class B : public A {
  int j;
public:
  int f();
  B() : A(f()),  // undefined: calls member function
                 // but base A not yet initialized

  j(f()) { }    // well-defined: bases are all initialized
};

但是,对 b 本身的访问和赋值是可以的,因为它具有空洞的初始化,并且一旦为其获取存储(这发生在构造函数调用开始之前很久),它的生命周期就开始了。因此

class B : public A {
private:
  int b;

public:
  B() : A(b=0) {}
};

定义明确。