记一次懵懂的源码阅读经历————static_cast,priavte继承与friend杂烩

前言

 
由于工作需要,最近在读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_caststatic_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
17
class 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
23
template<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
23
template<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
24
template<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
}