先回答你的问题
首先一个观念, shared_ptr 跟 unique_ptr 的设计
就是你可以把这个变量“当成指向物件的指标变量来使用”
例如对 Foo* pFoo = new Foo(); 可以 pFoo->bar();
那 shared_ptr<Foo> spFoo (new Foo()); 也就可以 spFoo->bar();
不过上面那句“当成”有个但书是“你不能对它进行指标算术 (包含阵列存取)”
因为概念上这些东西是用来管理一个物件的, 对这物件之外的地方存取没有意义
所以虽然可以 *spFoo 取得所管理的物件, 但不能写 spFoo[0] 做阵列存取
====
那么这里就要回到你一开始的指标版本了
你在宣告了 std::vector<std::vector<int>> *p = ...; 之后
下面直接使用了 p[0] 去取得这个物件
(像是 p[0].size() 去取得外层长度, 或 p[0][0] 取得第一列等等)
虽然你取到了, 但这只是因为它是指标变量所以 *p 等同 p[0] 而已
这里正确的取法是要使用 *p 去取得物件 (它又不指向一个阵列为什么要 [0] ?)
(也就是像上面那个要写成 (*p).size() 和 (*p)[0] 才对)
这即是造成了你换成 shared_ptr 之后编译不过的原因
不然理论上指标版本的程式码跟 shared_ptr / unique_ptr 版本的程式码
应该是无痛转换的才对
====
然而 (这里算是一部份的题外话)
使用动态配置产生一个 vector 物件是有点叠床架屋的
因为 vector 自己本身就是使用动态配置来配置它的阵列内容的
(也因此才能够随意增加长度)
vector 的本体其实相对很小, 没记错应该等同数个 int 大小而已
传来传去的成本也并不高
这也就是为什么推文一开始在问你“为什么不直接宣告 vector 物件”的原因
====
再延伸出去一点
相对于 shared_ptr / unique_ptr 是对“(动态配置物件的)指标”的包装
vector 则是对“(动态配置的)阵列”的包装
也就是设计上 vector 才是使用阵列的存取方式 (也就是 [] 的方式)
这同时也呼应到版上常常看到很多人在讲的“指标不等于阵列”这句话
对一般的指标来说, 虽然编译到底层的机械码是一样的, 但上层的语意不同
而 shared_ptr / unique_ptr 跟 vector 即是分别抓取这两种语意做为存取接口
因此在使用时不能混用这两种存取接口