亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

多態性的 C++ 模板?“使用類模板需要模板參數”

多態性的 C++ 模板?“使用類模板需要模板參數”

侃侃無極 2023-05-10 13:32:48
我是 C++ 的新手,正在嘗試弄清楚如何使用模板來構建特定類型的類的層次結構以實現多態性。我知道如何使用泛型在 Java 中很容易地做到這一點。我確定之前有人問過這個問題,但我只是不知道在 C++ 中使用什么術語來搜索我想要的內容。我希望能夠使用我創建的基類,該基類使用由下面定義的非常指定的類Derived_1和組成的對象組合創建Derived_2,每個類都從名為 的類擴展Base。但是,我收到一個編譯器錯誤提示我需要聲明類型?我使用什么語法來指示z在實現中使用的這個向量可以由從 擴展的類的任意組合組成Base?到目前為止,這是我所擁有的,我認為在聲明基類和擴展該基類的類時大部分是正確的:base_types.htemplate <typename T>class Base {public:    Base<T>(size_t a, T b) :            m_a(a),            m_b(b) {    }    T getB() const;    size_t m_a;    T m_b;};// specific kind of Base that uses boolclass Derived_1 : public Base<bool> {    Derived_1(uint32_t a);     // second parameter is unused, is assumed to be "true"    bool getB();};// specific kind of Base that uses size_tclass Derived_2 : public Base<size_t> {    Derived_2(uint32_t a, size_t b);    size_t getB();};基礎類型.cppDerived_1::Derived_1(uint32_t a) : Base(a, true) { }      // second parameter is unused, is assumed to be "true"bool Derived_1::getB() { return m_b; }Derived_2::Derived_2(uint32_t a, size_t b) : Base(a, b) { }bool Derived_2::getB() { return m_b; }impl_types.h#include "base_types.h"#include <vector>class Foo {public:    Foo(        size_t y,        const std::vector<Base>& z);      // Error: Use of class template 'Base' requires template arguments};impl_types.cppclass Foo {public:    Foo(        size_t y,        const std::vector<Base>& z) :      // Error: Use of class template 'Base' requires template arguments            m_y{y},            m_z{z};};作為參考,這是我想做的事情的 Java 實現:// Base.javaclass Base<T> {    int m_a;    T m_b;    Base(int a, T b) {        m_a = a;        m_b = b;    }    T getB() {        return m_b;    }}// Derived_1.javaclass Derived_1 extends Base<Boolean> {    Derived_1(int a, Boolean a) {        super(a, b);    }    Boolean getB() {        return m_b;    }}// Derived_2.javaclass Derived_2 extends Base<String> {    Derived_2(int a, String b) {        super(a, b);    }    String getB() {        return m_b;    }}
查看完整描述

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>, ...>

查看完整回答
反對 回復 2023-05-10
?
天涯盡頭無女友

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>>當您希望它們按值在同一向量中時應該使用。動態多態對象必須具有完全相同的基類,即使這樣您也需要通過引用而不是值將它們放入容器中。


查看完整回答
反對 回復 2023-05-10
?
回首憶惘然

TA貢獻1847條經驗 獲得超11個贊

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



查看完整回答
反對 回復 2023-05-10
?
慕蓋茨4494581

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)。



查看完整回答
反對 回復 2023-05-10
  • 4 回答
  • 0 關注
  • 203 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號