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

时间:2015-05-09 19:50:40

标签: c++ constructor undefined-behavior clang++

请考虑以下代码:

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也没有。

2 个答案:

答案 0 :(得分:7)

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

  

在非委托构造函数中,初始化按以下顺序进行:
   - 首先,仅适用于派生程度最高的类(1.8)的构造函数,虚拟基类...
   - 然后,直接基类按声明顺序初始化,因为它们出现在base-specifier-list中   (无论mem-initializers的顺序如何)    - 然后,非静态数据成员按照在类定义中声明的顺序进行初始化   (再次不管mem-initializers的顺序如何)。

b基类初始化时,

A将不会被初始化。由于同样的原因,赋值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.
    { };
};

答案 1 :(得分:6)

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

  

成员函数(包括虚拟成员函数,10.3)可以   要求建造一个物体。同样,一个对象下   构造可以是typeid运算符(5.2.8)或a的操作数   dynamic_cast(5.2.7)。 但是,如果这些操作是在a中执行的   在所有 mem-initializers 之前, ctor-initializer (或直接或间接从 ctor-initializer 中调用的函数)   基类已经完成,操作的结果是   undefined。 [例如:

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) {}
};

定义明确。