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() {}
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章