最近读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() == falseFunctionDecl::isOutOfLine() == false && FunctionDecl::isInlined() == trueFunctionDecl::isOutOfLine() == true && FunctionDecl::isInlined() == falseFunctionDecl::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() {}
};
Clang源码解析:isOutOfLine与isInlined

本文深入解析Clang源码中FunctionDecl::isOutOfLine()和FunctionDecl::isInlined()两个方法的具体实现与含义,揭示它们在函数声明与定义上下文中的作用,以及如何判断函数是否为内联或外部定义。
1443

被折叠的 条评论
为什么被折叠?



