前言
由于工作需要,最近在读folly库src,在阅读源码过程中产生了一些关于private继承,static_cast,friend之间的疑问。
前置知识
private继承
C++在业务逻辑中很少涉及private继承,实际上private继承仅仅是一种实现技术,如果class d以private形式继承class b,这说明d仅仅为了采用某些b中已经存在的特性,而不是b和d有任何观念上的交集。
一般来说,private继承主要用于EBO(Empty Base Optimization)。
更多信息可以参考Effective C++中关于private继承的说明。
static_cast
四种类型转换之一,相较于dynamic_cast
,static_cast
在做向下类型转换时(Base class to Derived class),不验证正确性。
当用作向下转换时,static_cast
需要保证待转换对象间存在public继承关系,否则无法通过编译。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class B {
};
class D1 : public B {
};
class D2 : private B {
};
int main() {
B *b = new B();
D1 *d1 = static_cast<D1*>(b); // ok
D2 *d2 = static_cast<D2*>(b); // error, cannot cast private base class 'B' to 'D2'
}
case
根据前置知识,下文内的代码无疑是无法通过编译的:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23template<typename T>
class Derived; // forward declaration
template<typename T>
class Base {
public:
void dosth() {
auto d = *(static_cast<Derived<T> *>(this)); // error, cannot cast private base class 'Base<T>' to 'Derived<T>'
return;
}
};
template<typename T>
class Derived final : private Base<T> {
};
int main() {
Base<int> b;
b.dosth();
}
但是,如果在Derived
内添加friend base class
,则可正常通过编译1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23template<typename T>
class Derived; // forward declaration
template<typename T>
class Base {
public:
void dosth() {
auto d = *(static_cast<Derived<T> *>(this)); // ok
return;
}
};
template<typename T>
class Derived final : private Base<T> {
friend class Base<T>; // add friend
};
int main() {
Base<int> b;
b.dosth();
}
疑问
我对friend
的了解仅仅是改变了class之间的符号访问关系,为什么通过增加friend修饰,可以做到cast private base to derived?
一些猜想与结论
根据 https://stackoverflow.com/questions/56673706/inaccessible-base-class-despite-friendship
得到了一些初步的结论:
简单来说,private继承令d无法访问b的内部具体实现(这也是private的特性),从而d*
与b*
不具备相互转换的能力(inaccessible-base-class)。
但加上friend后,在指定的上下文内d可以访问b的内部实现,因此static_cast是合法的。示例如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24template<typename T>
class Derived; // forward declaration
template<typename T>
class Base {
};
void foo2();
template<typename T>
class Derived final : private Base<T> {
friend class Base<T>;
friend void foo2();
};
void foo() {
Base<int> b;
Derived<int> * d1 = static_cast<Derived<int> *>(&b); // error
}
void foo2() {
Base<int> b;
Derived<int> * d1 = static_cast<Derived<int> *>(&b); // ok
}