最近读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() {}
};