
C++
为何
C++没有像
Python等语言那般活跃的生态?在我看来,根本原因是
C++未能妥善解决菱形依赖这一问题。那么什么是菱形依赖?举个例子,存在A、B、C、D四个库(这里限定D不是STL之类的标准库,且B、C、D都是以源码形式发布的
C++开源库),A依赖B,A也依赖C,而B和C同时依赖D。在生态活跃的其他语言里,这种情况极为常见。但在
C++中会出现诸多问题。比如,B和C中至少有一个会把D的源码合并到自己的源码里,放在类似thirdparty的目录下。A把这两份源代码和自己的源代码一同放到thirdparty目录后进行编译,就会产生符号冲突,从而导致编译失败。又比如,B和C在安装说明里提到,必须把依赖放置在某个特定位置,然后修改Makefile、CMAKE或者Bazel配置,把D的源码目录加进来。可这样一来,B和C编译出来的.a文件中依然带着D的全部符号,A尝试进行二进制链接时,就会因为符号冲突而失败。再如,B和C在安装说明中要求将D的依赖放在某处,先编译成.a文件,并设置好参数直接链接.a文件。然而,B和C对D要求的编译参数不同,导致无法链接在一起,编译失败。要是把所有依赖一个个放在独立的位置,设置好include目录,每个都用自己的Makefile单独编译成.a文件,且使用同一套编译参数,最后再逐个指定链接在一起,这样做的话人工成本就会非常高。A希望B和C通过动态链接的方式连接,这样便于升级。可是如果动态链接库中链接了D的符号,加载时就会产生符号冲突,导致失败。若B和C一个动态链接D,一个静态链接D,同样会出现符号冲突而失败。即使D把自己改成了仅含头文件的库,不需要单独编译了,但B和C在引用D时设置的宏不一样,这会使编译出的弱符号不兼容,链接后运行时就会崩溃。综上所述,对于
C++库的作者而言,最终就面临这样一种状况:若要提供一个好用、易于编译且不会经常出问题的库,那就不能引用其他开源库。没有比这更矛盾的事情了,想要创建新的库,首要之事却是不能使用别人的库。但如果要正经开发一个系统,谁能保证自己有朝一日不会把代码重构成一个通用的库?如此一来,从第一天开始就得避免使用第三方开源库。