析构函数中奇怪的枚举

时间:2015-11-19 10:09:07

标签: c++ enums

目前,我正在阅读Protocol Buffer的源代码,我发现了一个奇怪的enum代码here

  ~scoped_ptr() {
    enum { type_must_be_complete = sizeof(C) };
    delete ptr_;
  }

  void reset(C* p = NULL) {
    if (p != ptr_) {
      enum { type_must_be_complete = sizeof(C) };
      delete ptr_;
      ptr_ = p;
    }
  }

为什么在这里定义enum { type_must_be_complete = sizeof(C) };?它用于什么?

4 个答案:

答案 0 :(得分:81)

这个技巧通过确保在编译析构函数时可以使用C的定义来避免UB。否则编译将失败,因为无法确定GLuint programhandle=glCreateProgram(); glAttachShader(programhandle, vertex); glAttachShader(programhandle, fragment); glLinkProgram(programhandle); GLint status; position=glGetAttribLocation(programhandle,"position"); color=glGetAttribLocation(programhandle, "color"); uniformmatrix=glGetUniformLocation(programhandle, "matrix"); fulltransform=glGetAttribLocation(programhandle, "fulltransform"); glEnableVertexAttribArray(position); glEnableVertexAttribArray(color); glEnableVertexAttribArray(fulltransform); glEnableVertexAttribArray(fulltransform+1); glEnableVertexAttribArray(fulltransform+2); glEnableVertexAttribArray(fulltransform+3); glGetProgramiv(programhandle , GL_LINK_STATUS, &status); if (status==GL_FALSE) { NSLog(@"program"); } glDeleteShader(vertex); glDeleteShader(fragment); glUseProgram(programhandle); 不完整类型(前向声明的类型),但可以使用指针。

在编译的二进制文件中,此代码将被优化掉,并且不起作用。

请注意:从5.3.5 / 5中删除不完整类型可能是未定义的行为:。

  

如果要删除的对象在此处具有不完整的类类型   删除和完整的类有一个非平凡的析构函数或a   解除分配功能,行为未定义

sizeof甚至发出以下警告:

  

警告:在调用delete时检测到可能的问题   操作员:
警告:'p'类型不完整
警告:前进   声明'struct C'

答案 1 :(得分:31)

如果sizeof(C)不是完整类型,

C将在编译时失败。设置本地范围enum使语句在运行时是良性的。

这是程序员保护自己的一种方式:如果一个不完整类型的后续delete ptr_具有非平凡的析构函数,则该行为是未定义的。

答案 2 :(得分:28)

要理解enum,首先要考虑没有它的析构函数:

~scoped_ptr() {
    delete ptr_;
}

其中ptr_C*。如果此时类型C不完整,即编译器知道的所有内容都是struct C;,那么(1) 默认生成的 - 没有析构函数用于指向的C实例。对于由智能指针管理的对象,这不太可能是正确的做法。

如果通过指向不完整类型的指针进行删除始终具有未定义行为,则标准可能只需要编译器对其进行诊断并失败。但是当真正的析构函数是微不足道的时候它是明确定义的:程序员可以拥有的知识,但编译器没有。为什么语言定义并允许这超出我的范围,但C ++支持许多当前不被视为最佳实践的实践。

完整类型具有已知大小,因此sizeof(C)将编译,当且仅当C是完整类型时 - 使用已知的析构函数。所以它可以用作守卫。一种方法就是

(void) sizeof(C);  // Type must be complete

我会猜测通过一些编译器和选项,编译器会在它注意到它不应该编译之前对其进行优化,并且enum是一种避免这种非编译的方法 - 符合编译器行为:

enum { type_must_be_complete = sizeof(C) };

选择enum而不仅仅是丢弃的表达的另一种解释只是个人偏好。

或者正如James T. Hugget建议in a comment给出这个答案,“枚举可能是一种在编译时创建伪可移植错误消息的方法。”

(1)对于不完整类型,默认生成的do-nothing析构函数是旧std::auto_ptr的问题。由国际C ++标准化委员会主席Herb Sutter撰写的a GOTW item about the PIMPL idiom进入{{3}}是如此阴险。当然,现在不推荐使用std::auto_ptr,而是使用其他机制。 功能

答案 3 :(得分:3)

可能确保定义C的技巧。