2011-05-25 79 views
6
struct A {}; 
struct B 
{ 
    B (A* pA) {} 
    B& operator = (A* pA) { return *this; } 
}; 

template<typename T> 
struct Wrap 
{ 
    T *x; 
    operator T*() { return x; } 
}; 

int main() 
{ 
    Wrap<A> a; 
    B oB = a; // error: conversion from ‘Wrap<A>’ to non-scalar type ‘B’ requested 
    oB = a; // ok 
} 

oB被構建時,爲什麼B::B(A*)不被Wrap<T>::operator T()調用? [注:B::operator = (A*)Wrap<T>::operator T()的下一個語句中被調用]爲什麼不給構造函數調用給定的鑄造操作符?

+0

哪個編譯器?在VS2010上編譯得很好。 – 2011-05-25 06:17:30

+0

@Agnel Kurian,那是因爲VS2010對C++ 0x的部分支持。 – 2011-05-25 06:20:09

+0

@Agnel,它與g ++ 4.4.1。 – iammilind 2011-05-25 06:20:33

回答

10

的問題是,被隱含地調用用戶定義的轉換的數量是有限的(1)由標準。

B ob = a; 

意味着兩個用戶轉換:

  • aWrap<A>::operator A*()應該叫
  • 的結果:B::B(A*)應該叫

@詹姆斯甘孜的解釋:此語法被稱爲「複製初始化」,實際上相當於B ob = B(a)(副本大部分時間都會被刪除)。這不同於B ob(a)這是一個「直接初始化」,並會工作。

如果明確限定任何的這個,它會工作,例如:

B ob = B(a); 

在另一方面,對於第二種情況是沒有問題:

ob = a; 

是短期手爲:

ob.operator=(a); 

因此只需要一個用戶定義的轉換,這是允許的。

編輯

,因爲它是在註釋中被要求(以基里爾的答案),我們可以在動機採取猜測。

鏈式轉換可能很長,很長,因此:

  • 可以驚喜的用戶 - 隱式轉換可能已經是令人驚訝的,因爲它是...
  • 可能會導致指數查找可能性(對於編譯器) - 它需要從兩端去,試圖檢查所有可能的轉換,並以某種方式「加入」這兩者(儘可能使用最短路徑)。

此外,只要轉化次數超過1次,您就有可能發現需要檢測的週期(即使可能不需要診斷,並且需要實施質量)。所以,由於限制是必要的,以避免無限長的搜索(它可能沒有被明確規定,最低要求),並且從1以後我們可能會有新的問題(週期),那麼1似乎是一個很好的限制儘管如此。

+1

+1正確答案。該標準不允許鏈接轉換。它允許在表達式中進行一次用戶轉換。 – Nawaz 2011-05-25 06:23:33

+0

這個答案只是部分正確。這裏的關鍵是「複製初始化」的語義,而不是「直接初始化」。 – 2011-05-25 08:16:52

2

這是因爲C++標準只允許一個用戶定義的轉換。根據§ 12.3/4:

至多一個用戶定義的轉化率(或構造轉換函數)被隱式應用於單個 值。

B oB = a; // not tried: ob(a.operator T*()), 1 conversion func+1 constructor 
oB = a; // OK: oB.operator=(a.operator T*()), 1 conversion func+1 operator= 

如可以使用調用構造的明確形式的替代方法:

B oB(a); // this requires only one implicit user-defined conversion 
+1

你能詳細一點嗎?特別是不允許它背後的動機是什麼。 – iammilind 2011-05-25 06:22:26

5

本標準不允許鏈接隱含的轉換。如果允許,那麼你可以寫這樣的代碼:

struct A 
{ 
    A(int i) //enable implicit conversion from int to A 
}; 
struct B 
{ 
    B(const A & a); //enable implicit conversion from A to B 
}; 
struct C 
{ 
    C(const B & b); //enable implicit conversion from B to C 
}; 
C c = 10; //error 

你不能指望10將轉換爲A,然後將轉換爲B,然後轉換爲C


B b = 10; //error for same reason! 

A a = 10;  //okay, no chained implicit conversion! 
B ba = A(10); //okay, no chained implicit conversion! 
C cb = B(A(10)); //okay, no chained implicit conversion! 
C ca = A(10); //error, requires chained implicit conversion 

該規則同樣適用於轉換調用operator T()含蓄。

考慮這一點,

struct B {}; 

struct A 
{ 
    A(int i); //enable implicit conversion from int to A 
    operator B(); //enable implicit conversion from B to A 
}; 

struct C 
{ 
    C(const B & b); //enable implicit conversion from B to C 
}; 

C c = 10; //error 

你不能指望10將轉換爲A,然後將轉換爲B(使用operator B()),然後轉換爲C。 S

這樣鏈接隱含轉換是不允許的。你必須這樣做:

C cb = B(A(10); //okay. no chained implicit conversion! 
C ca = A(10); //error, requires chained implicit conversion 
7

這是因爲你正在使用「複製初始化」。如果你寫的 聲明oB

B oB(a); 

,它應該工作。兩種初始化的語義是 不同。對於B oB(a),編譯器會嘗試查找可以使用給定參數調用的構造函數 。在這種情況下,可以調用B::B(A*) ,因爲存在從Wrap<A>A*的隱含轉換。對於B oB = a,語義是隱式地將a轉換爲 類型B,然後使用B的複製構造函數初始化oB。 (該 實際拷貝可以被優化了,但該程序的合法性是 確定,如果它不是。)還有是 Wrap<A>沒有隱式轉換到B,只有Wrap<A>A*

該作業當然是有效的,因爲賦值運算符 需要A*,所以隱式轉換髮揮作用。

+0

+1好點提出。我有一種感覺,即「B oB = a;」和「B oB(a)」是相同的! – iammilind 2011-05-25 08:24:14

+0

+1000。尼斯。我不知道兩個初始化的語義之間的區別。 – Nawaz 2011-05-25 08:43:10

相關問題