[英]C++ Operator overloading - casting from class
在將Windows代碼移植到Linux時,我在GCC 4.2.3中遇到以下錯誤消息。 (是的,我知道這是一個舊的版本,但我不能輕易升級。)
main.cpp:16: error: call of overloaded ‘list(MyClass&)’ is ambiguous
/usr/include/c++/4.2/bits/stl_list.h:495: note: candidates are: std::list<_Tp, _Alloc>::list(const std::list<_Tp, _Alloc>&) [with _Tp = unsigned char, _Alloc = std::allocator<unsigned char>]
/usr/include/c++/4.2/bits/stl_list.h:484: note: std::list<_Tp, _Alloc>::list(size_t, const _Tp&, const _Alloc&) [with _Tp = unsigned char, _Alloc = std::allocator<unsigned char>]
我正在使用以下代碼生成此錯誤。
#include <list>
class MyClass
{
public:
MyClass(){}
operator std::list<unsigned char>() const { std::list<unsigned char> a; return a; }
operator unsigned char() const { unsigned char a; return a; }
};
int main()
{
MyClass a;
std::list<unsigned char> b = (std::list<unsigned char>)a;
return 0;
}
有沒有人遇到過這個錯誤? 更重要的是,如何繞過它? (通過使用諸如GetChar()
, GetList()
等函數,可以完全避免過載,但是我想避免這種情況。)
(順便說一句,刪除“ operator unsigned char()
”會刪除錯誤。)
模糊性來自對演員表達的解釋。
在選擇轉換時,編譯器首先考慮static_cast
樣式轉換,並考慮如何解析如下所示的初始化:
std::list<unsigned_char> tmp( a );
這種結構是模糊的,因為a
有一個用戶定義的轉換為std::list<unsigned char>
和unsigned char
和std::list<unsigned char>
都有一個構造函數,它接受const std::list<unsigned char>&
和一個構造函數,它接受size_t
(可以提升unsigned char
)。
當轉換為const std::list<unsigned_char>&
,會考慮初始化:
const std::list<unsigned_char>& tmp( a );
在這種情況下,當選擇用戶定義的轉換為std::list<unsigned_char>
,新引用可以直接綁定到轉換結果。 如果用戶定義的轉換為unsigned char
,則必須創建一個類型為std::list<unsigned char>
的臨時對象,這使得該選項的轉換順序比前一個選項更差。
我已將您的示例簡化為以下內容:
typedef unsigned int size_t;
template <typename T>
class List
{
public:
typedef size_t size_type;
List (List const &);
List (size_type i, T const & = T());
};
typedef List<unsigned char> UCList;
class MyClass
{
public:
operator UCList const () const;
operator unsigned char () const;
};
void foo ()
{
MyClass mc;
(UCList)mc;
}
第一點是,標准定義了C風格的static_cast
應該使用更合適的C ++樣式轉換,在這種情況下,它是static_cast
。 所以上面的演員相當於:
static_cast<UCList> (mc);
static_cast的定義是:
一個表達式e可以使用顯式轉換為一個類型T
static_cast
形式static_cast<T>(e)
,如果聲明"T t(e);"
形成良好,對於一些發明的臨時變量t(8.5)
因此,演員的語義與以下內容相同:
UCList tmp (mc);
從13.3.1.3開始,我們得到了可以在UCList
使用的候選構造函數UCList
:
UCList (UCList const &) #1
UCList (size_type, T const & = T()); #2
接下來會發生兩個獨立的重載決策步驟,每個步驟對應一個轉換運算符。
轉換為#1:目標類型為UCList const &
,重載UCList const &
在以下轉換運算符之間進行選擇:“ operator UCList const ()
”和“ operator unsigned char ()
”。 使用unsigned char
需要額外的用戶轉換,因此對於此重載步驟不是可行的函數。 因此重載解析成功並將使用operator UCList const ()
。
轉換為#2:目標類型為size_t
。 默認參數不參與重載決策。 重載分辨率再次在轉換運算符之間進行選擇:“ operator UCList const ()
”和“ operator unsigned char ()
”。 這次沒有從UCList
到unsigned int
轉換,因此這不是一個可行的功能。 unsigned char
可以提升為size_t
,所以這次重載解析成功並將使用“ operator UCList const ()
”。
但是,現在回到頂層,有兩個獨立的獨立重載UCList
步驟已成功從mc
轉換為UCList
。 結果因此含糊不清。
為了解釋最后一點,這個例子與正常的重載分辨率情況不同。 通常,參數和參數類型之間存在1:n的關系:
void foo (char);
void foo (short);
void foo (int);
void bar() {
int i;
foo (i);
}
這里有i=>char
, i=>short
和i=>int
。 通過重載分辨率比較這些,並選擇int
過載。
在上面的例子中,我們有一個m:n的關系。 該標准概述了為每個單獨的參數和所有'n'參數選擇的規則,但這就是它結束的地方,它沒有說明我們應該如何決定使用不同的'm'參數。
希望這有一定道理!
更新:
這里有兩種初始化語法:
UCList t1 (mc);
UCList t2 = mc;
't1'是直接初始化(13.3.1.3), 所有構造函數都包含在重載集中。 這幾乎就像擁有多個用戶定義的轉換。 有一組構造函數和一組轉換運算符。 (即m:n)。
在't2'的情況下,語法使用復制初始化(13.3.1.4)和規則不同:
在8.5中指定的條件下,作為類類型對象的復制初始化的一部分,可以調用用戶定義的轉換以將初始化表達式轉換為要初始化的對象的類型。 重載決策用於選擇要調用的用戶定義的轉換
在這種情況下,只有一個類型, UCList
,因此只有一組轉換運算符重載要考慮,即。 我們不考慮UCList的其他構造函數。
如果刪除強制轉換,它會正確編譯,我已經檢查過運算符std :: list正在執行。
int main()
{
MyClass a;
std::list<unsigned char> b = a;
return 0;
}
或者,如果將其強制轉換為const引用。
int main()
{
MyClass a;
std::list<unsigned char> b = (const std::list<unsigned char>&)a;
return 0;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.