最近讀clang
源碼時發現這麼一段代碼:
FunctionDecl *FD = ......
......
if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
......
if (MD->isOutOfLine() && ......) {
......
if (FD->isInlined() && ......) {
......
}
......
}
}
雖說CXXMethodDecl
和FunctionDecl
是繼承關係,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
:一個是LexicalDC
(getLexicalDeclContext()
的返回值),一個是SemanticDC
(getDeclContext()
的返回值),這兩個context
可能相同,也可能不同:
// 1
namespace A {
void f(); // SemanticDC == LexicalDC == 'namespace A'
}
// 2
void A::f(); // SemanticDC == namespace 'A'
// LexicalDC == global namespace
對於情況1,LexicalDC
與SemanticDC
相同,均爲A命名空間;對於情況2,LexicalDC
爲全局命名空間,SemanticDC
爲A命名空間。
那這樣的話FunctionDecl::isOutOfLine()
含義就清楚了:當一個函數聲明的LexicalDC
與SemanticDC
不同時,該函數返回true
,LexicalDC
與SemanticDC
相同時,該函數返回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種情況:
- 函數聲明被標記爲
inline
- 函數聲明被標記爲
constexpr
- 成員函數體定義在
class
內部
總結
現在我們可以枚舉出這兩個函數返回值所有組合對應的代碼:
FunctionDecl::isOutOfLine() == false && FunctionDecl::isInlined() == false
FunctionDecl::isOutOfLine() == false && FunctionDecl::isInlined() == true
FunctionDecl::isOutOfLine() == true && FunctionDecl::isInlined() == false
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() {}
};