編譯器不接受這個的原因是標準告訴它不要。
標準告訴它不要的原因是委員會沒有要介紹的規則const MyTemplate<Derived*>
是與const MyTemplate<Base*>
相關的類型,即使非常量類型不相關。他們當然不希望爲std :: set設置一個特殊的規則,因爲通常這種語言不會爲庫類創建特殊的情況。
標準委員會不想讓這些類型相關的原因是MyTemplate可能沒有容器的語義。試想一下:
template <typename T>
struct MyTemplate {
T *ptr;
};
template<>
struct MyTemplate<Derived*> {
int a;
void foo();
};
template<>
struct MyTemplate<Base*> {
std::set<double> b;
void bar();
};
那麼,什麼是它甚至意味着傳遞一個const MyTemplate<Derived*>
作爲const MyTemplate<Base*>
?這兩個類沒有共同的成員函數,並且不兼容佈局。你需要在兩者之間有一個轉換運算符,否則編譯器不知道要做什麼,無論它們是否是const。但是在標準中定義模板的方式,即使沒有模板專業化,編譯器也不知道該做什麼。
std::set
本身可以提供一個轉換操作符,但是這隻需要創建一個副本(*),您可以輕鬆地完成自己的操作。如果有這樣的東西,如std::immutable_set
,那麼我認爲有可能實現這樣的一個std::immutable_set<Base*>
可以通過指向相同的pImpl從std::immutable_set<Derived*>
構建。即便如此,如果派生類中有非虛擬運算符重載 - 基本容器將調用基本版本,那麼奇怪的事情將會發生,因此如果轉換可能會對該集合進行排序,前提是它有一個非默認比較器對象本身而不是地址。所以轉換會帶來沉重的警告。但無論如何,並不存在immutable_set
,而const與不可變的不一樣。
此外,假設Derived
通過虛擬或多重繼承與Base
有關。然後,您不能重新解釋Derived
的地址作爲Base
的地址:在大多數實現中,隱式轉換會更改地址。因此,您不能將包含Derived*
的結構批量轉換爲包含Base*
的結構而不復制結構。但是C++標準實際上允許這發生在任何非POD類中,而不僅僅是多重繼承。而且Derived
是非POD,因爲它有一個基類。所以爲了支持std::set
的這種變化,繼承和結構佈局的基本原理將不得不改變。這是C++語言的一個基本限制,即標準容器不能以您想要的方式重新解釋,而且我也沒有意識到可以在不降低效率或可移植性的情況下實現它們的任何技巧。這很令人沮喪,但這件事很難。
由於你的代碼是按值傳遞反正一組,你可以只讓該副本:
std::set<Derived*> objs;
register_objects(std::set<Base*>(objs.begin(), objs.end());
[編輯:你已經改變了你的代碼示例不按值傳遞。我的代碼仍然有效,而且據我所知是你可以做其他的比重構調用代碼首先使用std::set<Base*>
最好的。]
寫作std::set<Base*>
的包裝,以確保所有的元素都是Derived*
,順便Java泛型工作,比安排想要高效轉換更容易。所以,你可以這樣做:
template<typename T, typename U>
struct MySetWrapper {
// Requirement: std::less is consistent. The default probably is,
// but for all we know there are specializations which aren't.
// User beware.
std::set<T> content;
void insert(U value) { content.insert(value); }
// might need a lot more methods, and for the above to return the right
// type, depending how else objs is used.
};
MySetWrapper<Base*,Derived*> objs;
// insert lots of values
register_objects(objs.content);
(*)其實,我想這可能寫入時複製,這在典型的方式使用常量參數的情況下,就意味着它永遠不會需要做的複製。但是,寫入時複製在STL實現中有點不受信任,即使這不是我懷疑委員會會要求這樣一個重量級的實現細節。
我糾正了參考通過,這是我的問題中的一個錯字,在我的代碼中,我做了這個正確的。 – 2009-07-13 11:05:20