4

假設我有一類C++與shared_ptr的模板實例爲const牛逼

template <typename T> 
class A { 
public: 
    template <typename V> 
    void f(std::tr1::shared_ptr<const std::vector<V> > v1, 
      std::tr1::shared_ptr<const std::vector<float> > v2) {} 
}; 

下無法編譯:

A<string> a; 
    std::tr1::shared_ptr<std::vector<float> > v1(new std::vector<float>()); 
    std::tr1::shared_ptr<std::vector<float> > v2(new std::vector<float>()); 
    a.f(v1, v2); 

編譯器錯誤是:

error: no matching function for call to 'A<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::f(std::tr1::shared_ptr<std::vector<float, std::allocator<float> > >&, std::tr1::shared_ptr<std::vector<float, std::allocator<float> > >&)' 

編譯器無法將std::tr1::shared_ptr<std::vector<float> >轉換爲 std::tr1::shared_ptr<const std::vector<float> >第一個論點。然而它可能是第二個(非模板參數)。

解決此問題的一個方法是將呼叫更改爲f(),將其稱爲f<float>(...)。 另一個解決方案是將v1聲明爲shared_ptr到const vector<float>.

問題1)爲什麼模板實例在這裏表現如此不同?

問題2)我對將一個shared_ptr用作方法參數的理解是方法 無法更改shared_ptr指向的內容。如果我們將shared_ptr改爲原始指針,並將v1,v2更改爲向量的原始指針,那麼代碼將很好地編譯。關於打破模板推演的shared_ptrs,什麼是 ?

+0

第三種解決方案是將'v1'更改爲'std :: tr1 :: shared_ptr '。 – sbi 2011-04-08 20:03:49

+0

但我不想這樣做,因爲我想確保f不會以任何方式改變參數。所有可以改變的是我如何在調用代碼a.f(...)中聲明v1和v2。 – Amitabha 2011-04-08 20:09:51

+0

我沒想到你會喜歡這個,只是你可能的解決方案的列舉看起來不完整。 ':)' – sbi 2011-04-08 20:24:41

回答

3

爲什麼模板實例在這裏表現如此不同?

A shared_ptr<T>可通過轉換構造函數隱式轉換爲shared_ptr<const T>。不幸的是,這種轉換不能用於模板參數推演。

爲了使參數推導成功,參數的類型必須完全匹配參數的類型;大多數轉換,包括用戶定義的轉換,都沒有使用。

只允許進行一些非常基本的轉換,包括數組到指針的衰減和函數到函數指針的轉換(因爲沒有函數參數可以是數組類型或函數類型,所以這些轉換不會導致混淆) 。另外,頂級const和volatile限定符完全被忽略。

+0

「_參數的類型必須**完全匹配參數的類型** _」否,可以應用限定和派生到基準的轉換。 「_top級const和volatile限定符完全被忽略。」在哪裏? – curiousguy 2011-12-15 03:08:26

+0

@curiousguy:這是不正確的。在參數扣除期間不允許派生到基礎的轉換。最基本的例子是struct B {}; struct D {};模板 void f(T x,T y){} int main(){f(B(),D()); }',即使派生到基礎的轉換可以解決歧義性,但是由於含糊不清而失敗,如果允許的話。 「頂級限定符」是那些沒有嵌套在類型中的。例如,'int * const'中的'const'是頂層的,而'int const *'中的'const'則不是。 – 2011-12-15 04:29:11

+0

「_這是不正確的。」我寫的是完全正確的:我寫的「可以應用」而不是「總是應用」。 「儘管派生到基礎的轉換可以解決歧義性問題。」這不是一個模棱兩可的問題,它是一個矛盾:'T' ='B'和'T' ='D'。 「_」頂級限定符「是那些沒有嵌套在類型中的。」我知道。頂級限定詞「完全被忽略」在哪裏?什麼時候?這比你描述的要複雜一些。 – curiousguy 2011-12-15 04:34:44

4

相關類型?

如您所知,T*const T*是相關類型。從第一個到第二個(資格轉換)有一個標準轉換。

首先,你必須認識到A<T>A<const T>不是一般相關的類型。這些類型可以具有不同的大小,表示和目的。一個可以被定義,但不是另一個。

特別是,A<const T>不是合格的A<T>,所以兩者之間沒有質量轉換,而且C++的靈活性不足以聲明用戶定義的質量轉換。 (用戶無法聲明任何標準轉換,只有用戶定義的轉換 - 用戶定義的轉換不是標準轉換。)

因此,用戶定義的類型與基本類型有根本的區別:它們不能與基本類型相關。

shared_ptr<>

shared_ptr<>被設計成一個系列兼容類型:shared_ptr<T>隱式轉換爲shared_ptr<U>當且僅當T*隱式轉換爲U*。特別是,shared_ptr<T>可以隱式轉換爲shared_ptr<const T>。它也可以隱含地轉換爲shared_ptr<void>,所以原因相同。

由於類型shared_ptr<const T>shared_ptr<T>不以任何特殊的方式相關,從shared_ptr<T>shared_ptr<const T>的轉換不是區別於shared_ptr<T>shared_ptr<void>。這些僅僅是兩種不同的轉換,但在任何情況下均不被認爲是「優選的」,不同於從T*const T*(等級=完全匹配)的轉換,其優於從T*void*(等級=轉換)的轉換。

函數模板

一些標準的轉換是允許的推導模板函數參數:

  • 資格轉換
  • 一些指針轉換:導出到基地
  • ...

但是,正如我們所見,0123之間不存在這樣的轉換類型。

這意味着即使如果編譯器被允許枚舉所有可能類型的模板參數打開一個函數模板

template <typename V> 
void f (shared_ptr<const T> v1); 

成一組無限函數原型:

for every type T, 
such that shared_ptr<const T> can be instantiated: 
f (shared_ptr<const T>) 

除非你有一個完全匹配,你將無法調用函數:給定的聲明

struct Base {}; 
struct Derived : Base {}; 

shared_ptr<Derived> d; 

該組f原型中,有:

f (shared_ptr<const Base>) 
f (shared_ptr<const Derived>) 

所以f (d)呼叫將是模棱兩可這兩個候選的涉及不同的用戶定義的轉換。