clang:FunctionDecl::isOutOfLine()和FunctionDecl::isInlined()能同时返回true吗?

最近读clang源码时发现这么一段代码:

FunctionDecl *FD = ......
......
if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
      ......
      if (MD->isOutOfLine() && ......) {
        ......
        if (FD->isInlined() && ......) {
          ......
        }
        ......
      }
    }

虽说CXXMethodDeclFunctionDecl是继承关系,isOutOfLine()也是虚函数,但是CXXMethodDecl并没有对其进行override,所以MD->isOutOfLine()实际上调用的还是FunctionDecl::isOutOfLine()。那么问题来了:在代码片段中,第一个if语句调用了FunctionDecl::isOutOfLine(),嵌套在第一个if中的第二个if调用了FunctionDecl::isInlined(),从方法名上看,这两个方法的返回值不该是互逆的吗?第一个返回true则第二个返回false,第一个返回false则第二个返回true,这样的话第二个if语句岂不是一个死分支了吗?

问题出在哪里呢?问题在于FunctionDecl::isOutOfLine()FunctionDecl::isInlined()的返回值是没有任何关联的,它们并不成互逆的关系,这两个方法的名字很容易让人望文生义。我们来看一下这两个方法的返回值究竟是什么含义。

FunctionDecl::isOutOfLine()

方法定义如下:

bool FunctionDecl::isOutOfLine() const {
  if (Decl::isOutOfLine())
    return true;

  // If this function was instantiated from a member function of a
  // class template, check whether that member function was defined out-of-line.
  if (FunctionDecl *FD = getInstantiatedFromMemberFunction()) {
    const FunctionDecl *Definition;
    if (FD->hasBody(Definition))
      return Definition->isOutOfLine();
  }

  // If this function was instantiated from a function template,
  // check whether that function template was defined out-of-line.
  if (FunctionTemplateDecl *FunTmpl = getPrimaryTemplate()) {
    const FunctionDecl *Definition;
    if (FunTmpl->getTemplatedDecl()->hasBody(Definition))
      return Definition->isOutOfLine();
  }

  return false;
}

总共有4个return,中间两个return是对FunctionDecl::isOutOfLine()的递归调用(虽说不是同一个对象),递归终止时的出口要么是第一个return true,要么是最后的return false。我们来看一下Decl::isOutOfLine()的定义:

bool Decl::isOutOfLine() const {
  return !getLexicalDeclContext()->Equals(getDeclContext());
}

getLexicalDeclContext()getDeclContext()返回值相等时返回false,否则返回true。那么getLexicalDeclContext()getDeclContext()又是什么呢:

  /// getLexicalDeclContext - The declaration context where this Decl was
  /// lexically declared (LexicalDC). May be different from
  /// getDeclContext() (SemanticDC).
  /// e.g.:
  ///
  ///   namespace A {
  ///      void f(); // SemanticDC == LexicalDC == 'namespace A'
  ///   }
  ///   void A::f(); // SemanticDC == namespace 'A'
  ///                // LexicalDC == global namespace
  DeclContext *getLexicalDeclContext() {
    if (isInSemaDC())
      return getSemanticDC();
    return getMultipleDC()->LexicalDC;
  }

getLexicalDeclContext()的注释写的很清楚,每个声明都有两个context:一个是LexicalDCgetLexicalDeclContext()的返回值),一个是SemanticDCgetDeclContext()的返回值),这两个context可能相同,也可能不同:

// 1
namespace A {
   void f(); // SemanticDC == LexicalDC == 'namespace A'
}

// 2
void A::f(); // SemanticDC == namespace 'A'
             // LexicalDC == global namespace

对于情况1,LexicalDCSemanticDC相同,均为A命名空间;对于情况2,LexicalDC为全局命名空间,SemanticDC为A命名空间。

那这样的话FunctionDecl::isOutOfLine()含义就清楚了:当一个函数声明的LexicalDCSemanticDC不同时,该函数返回trueLexicalDCSemanticDC相同时,该函数返回false。

FunctionDecl::isInlined()

方法定义为:

  /// Determine whether this function should be inlined, because it is
  /// either marked "inline" or "constexpr" or is a member function of a class
  /// that was defined in the class body.
  bool isInlined() const { return FunctionDeclBits.IsInline; }

注释说的很清楚,该函数返回值用来标识3种情况:

  1. 函数声明被标记为inline
  2. 函数声明被标记为constexpr
  3. 成员函数体定义在class内部

总结

现在我们可以枚举出这两个函数返回值所有组合对应的代码:

  1. FunctionDecl::isOutOfLine() == false && FunctionDecl::isInlined() == false
  2. FunctionDecl::isOutOfLine() == false && FunctionDecl::isInlined() == true
  3. FunctionDecl::isOutOfLine() == true && FunctionDecl::isInlined() == false
  4. FunctionDecl::isOutOfLine() == true && FunctionDecl::isInlined() == true
// 1
namespace A
{
    void f() {}
}

// 2
namespace B
{
    inline void f() {}
}

// 3
namespace C
{
    void f();
}

void C::f() {}

// 4
namespace D
{
    inline void f();
}

void D::f() {}

但是如果FunctionDecl对象是一个成员函数的话,似乎会少一种情况,因为以下两种情况都对应FunctionDecl::isOutOfLine() == false && FunctionDecl::isInlined() == true

// 1
class A
{
    void f() {}
};

// 2
class B
{
    inline void f() {}
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章