4 回答

TA貢獻1772條經驗 獲得超5個贊
在 Java 中,這是可行的,因為 aArrayList<Widget>基本上是ArrayList<Object>. 這有一些優點,但也有缺點。
在 C++ 中,有模板而不是泛型。出于相似的目標,它們在實現和含義方面有很大不同。
正如他們的名字所說:他們是模板。類模板不是類。它是某種只存在于編譯時的實體。只有當模板被實例化時,它才會變得具體。
ELI5關于實例化一個模板的過程:std::vector就是模板。std::vector<int>是模板的實例化。模板通過填充代碼的漏洞來實例化,由模板參數表示:
template<typename T>
auto value() -> T { return T{}; }
int main() {
? ? return value<int>() + value<short>();
}
實例化了兩個函數,看起來像這樣:
template<> //? ?v---- T has been replaced!
auto value() -> int { return int{}; }
template<> // Another instantiation
auto value() -> short { return short{}; }
當然,編譯器不會以文本方式替換它,而是使用 AST 來解析實際類型。
這是為了向您展示模板在該過程之后并不真正存在。只有實例化是具體的。并且有多個實例化。
就像上面的函數模板一樣,當一個模板類被實例化時,一個全新的類型就被創建了。實例化創建的類型完全不相關。它們只是不同的類型,就像模板函數產生不同的實例一樣。
那么...如果您有許多不同的、不相關的類,您如何實現多態性呢?
你加個接口!
struct Interface {
? ? // TODO: put useful function there
};
template<typename T>
struct Base : Interface {
? ? virtual auto getB() const -> T;
};
struct Impl1 : Base<bool> {
? ? auto getB() const -> T override;
};
如果相反,你的意圖是做這樣的事情:
Base b = ...;
// pseudocode
if (typeid(b->getB()) == bool)
{
? ? bool b = dynamic_cast<bool>(b->getB());
}
else if (typeid(b->getB()) == std::size_t)
{
? ? std::size_t b = dynamic_cast<std::size_t>(b->getB());
}
然后,像其他答案一樣,一個變體就是解決方案。Base如果你像這樣列出可能的類型,那么你就提前知道了可能類型的列表。所以 astd::variant就是你想要的。
為了使變體更易于使用,您始終可以為類型添加別名:
using VBase = std::variant<Base<bool>, Base<std::size_t>, ...>

TA貢獻1831條經驗 獲得超9個贊
你不能std::vector<Base>
因為Base
是模板。你應該有std::vector<Base<bool>>
,std::vector<Base<size_t>>
或者類似的。Base<bool>
并且Base<size_t>
是不同的類型,除了使用相同的模板制作之外沒有其他共同之處Base
。
可能是您想要的,std::vector<std::variant<Base<bool>,Base<size_t>>>
但很難從您的代碼中分辨出來。用于std::variant
具有可以具有不同類型值的變量(可能沒有共同點)。
您不能將派生類的對象放入基類的向量中,因此std::vector<std::variant<Derived1,Derived2>>
當您希望它們按值在同一向量中時應該使用。動態多態對象必須具有完全相同的基類,即使這樣您也需要通過引用而不是值將它們放入容器中。

TA貢獻1847條經驗 獲得超11個贊
如果你創建一個非模板化的空基類,一個從它繼承的模板,然后是特化,你可以將基指針存儲在一個向量或任何其他結構中。如果你想使用存儲的對象,你必須明確地將它們轉換回實際類型(如果我是正確的話,這也必須在 Java 中完成)。

TA貢獻1850條經驗 獲得超11個贊
錯誤:使用類模板Base
需要模板參數
你得到錯誤是因為它Base
不是一個類型,而只是一個模板。不要將 C++ 模板與 Java 泛型混淆,它們實際上是非常不同的概念。您不能擁有模板向量,因為模板只是模板。您需要實例化它們以獲得類型。例如,您可以有一個std::vector<Base<bool>>
.
您的代碼中的另一個問題是Base
應該有一個虛擬析構函數。否則你有內存泄漏的危險。必須聲明方法以virtual
啟用動態分派。
話雖如此...
你真正想做什么:
我只是想持有一個從公共基類繼承的不同類對象的向量,[...]
你不需要模板。這很簡單:
#include <iostream>
#include <vector>
#include <memory>
#include <utility>
struct base {?
? ? virtual void some_method(){ std::cout << "base\n";}
? ? virtual ~base(){}
};
struct foo : base {
? ? virtual void some_method() override { std::cout << "foo\n";}
};
struct bar : base {
? ? virtual void some_method() override { std::cout << "bar\n";}
};
int main() {
? ? std::vector<std::shared_ptr<base>> v;
? ? v.emplace_back(new foo());
? ? v.emplace_back(new bar());
? ? for (auto& e : v) e->some_method();
? ? return 0;
}
多態性適用于指針或引用。由于在容器中存儲引用并不是那么簡單,所以我使用了指針。我使用智能指針,因為我不想在手動內存管理上亂來。
到目前為止,一切都很好...
[...] 但是使用在那些返回不同類型的類之間共享的方法,具體取決于該特定類
然而,這并不容易。首先,注意同一個方法不能有不同的返回類型。舉個例子,如果你有
struct example_base {?
? ? virtual int foo() { return 1;}?
};
struct example_derived {
? ? virtual double foo() override { return 1.4; }
};
然后example_derived::foo不覆蓋!_ example_base::foo感謝override編譯器會通過一條錯誤消息告訴你
prog.cc:20:24: error: 'foo' marked 'override' but does not override any member functions
? ? ? ? virtual double foo() override { return 1.4; }
? ? ? ? ? ? ? ? ? ? ? ?^
根據您實際想要實現的目標(為什么您需要派生以不同的返回類型“共享一個通用方法”?),有不同的方法可以解決這個問題。我會告訴你一種方法。
#include <iostream>
#include <vector>
#include <memory>
#include <utility>
struct return_type_base {?
? ? virtual void print() {}
? ? virtual ~return_type_base() {}
};
struct bool_return_type : return_type_base {
? ? bool value = true;
? ? virtual void print() { std::cout << value << "\n"; }
};
struct int_return_type : return_type_base {
? ? int value = 3;
? ? virtual void print() { std::cout << value << "\n"; }
};
using return_type_ptr = std::shared_ptr<return_type_base>;
struct base {?
? ? virtual return_type_ptr some_method() = 0;
? ? virtual ~base(){}
};
struct foo : base {
? ? virtual return_type_ptr some_method() override {?
? ? ? ? return return_type_ptr(new bool_return_type());
? ? }
};
struct bar : base {
? ? virtual std::shared_ptr<return_type_base> some_method() override {?
? ? ? ? return return_type_ptr(new int_return_type());
? ? }
};
int main() {
? ? std::vector<std::shared_ptr<base>> v;
? ? v.emplace_back(new foo());
? ? v.emplace_back(new bar());
? ? for (auto& e : v) e->some_method()->print();
? ? return 0;
}
它基本上與上面的方法完全相同。為了以多態方式處理不同的類型,我們聲明了一個公共基類并處理該基類的共享指針。如前所述,這只是一種可能的方法。請對它持保留態度,它只是為了給你一個起點。主要缺點是:它是侵入性的(您必須為每個要返回的類型編寫一個類)并且它使用虛函數(即運行時開銷)。有更好的方法,但細節對您實際想要對這些不同的返回類型做什么很重要。為了進一步閱讀,我建議您搜索“type erasure”。
長話短說
模板是一個純粹的編譯時概念。如果你想在運行時平等對待不同的類型,你需要某種形式的運行時類型擦除??捎糜诖说募夹g在 Java 和 C++ 中有很大不同。通過虛函數實現的運行時多態性適用于兩者,并且可能是最容易理解的一種,尤其是當您來自 Java 時。您還應該看看標準庫必須提供的內容 (?std::any
,?std::variant
)。
添加回答
舉報