先回答你的問題
首先一個觀念, 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 即是分別抓取這兩種語意做為存取介面
因此在使用時不能混用這兩種存取介面