簡體   English   中英

CRTP 與基類向量

[英]CRTP vs. vector of base class

我有一個類似於CRTP 共享指針的 C++ 向量的問題,但我的問題公式增加了這樣一個事實,即我想用於所有繼承類的函數的返回類型是模板化的。

詳細地讓我們假設:

template <class Derived>
class Base {
    Derived Value() const {
        return static_cast<Derived>(this->Value());
    };
};

class ChildDouble : public Base<ChildDouble> {
  public:
    ChildDouble(double r) : _value(r){};

    double Value() const {
        return _value;
    };

  private:
    double _value;
};

class ChildString : public Base<ChildDouble> {
  public:
    ChildString(string s) : _value(s){};

    string Value() const {
        return _value;
    };

  private:
    string _value;
};

目標是使用它有點類似於以下主要

void main() {


    std::vector<Base*> vec;
    vec.push_back(new ChildDouble(3.0));
    vec.push_back(new ChildString("Thomas"));

    unsigned counter = 0;
    for (const auto& e : vec) {
        std::cout << "Entry " << counter << " : " << e->Value()
                  << std::endl;
        counter++;
    }

}

編譯器顯然對此不滿意,因為Base需要一個模板參數。

任何想法如何解決? 雖然我不應該使用它,但我在這里使用 CRTP 嗎?

虛擬方法(這是在沒有 CRTP 的情況下使上述工作通常需要的方法)在這里不起作用,因為每個派生類型中Value()的接口都不同。 虛擬繼承依賴於每個人的簽名都相同,除了一些特殊情況,比如協變返回類型。 它也不會工作,因為虛擬方法不能被模板化。

但是,您可以使用std::variant動態調度不兼容的接口,因為它基於模板。 首先,為您的變體定義一個方便的別名:

using Child = std::variant<ChildDouble, ChildString>;

然后使用,使用std::visit和通用 lambda 進行調度:

std::vector<Child> vec;
vec.push_back(ChildDouble(3.0));
vec.push_back(ChildString("Thomas"));

unsigned counter = 0;
for (const auto& e : vec) {
    std::visit([&counter](auto&& v) { 
        std::cout << "Entry " << counter << " : " << v.Value()
                << std::endl;
    }, e);

    counter++;
}    

演示: https : //godbolt.org/z/bENWYW

它不起作用,因為編譯器不知道您想將哪種類型放入向量中,而您需要指定它。 如果你嘗試vector<Base<double>*>vec; 它會起作用,但您不能將向量與其他類型(如 Base)一起使用,因為它是其他類型。 解決方案是使用 std::variant 或 std::any 代替模板。

現在你有了一個對象變體/任何 base 中的聲明值都會讓你的生活更輕松。

另外我建議你:

  • 不要使用以下划線“_”開頭的變量,因為編譯器的許多內部函數都使用這種語法。
  • 不要使用原始指針。 像 share_ptr 一樣使用 smart_ptr 那么你就不用擔心用 delete 來破壞它。

在帶有更改的代碼下方:

#include <memory>
#include <vector>
#include <string>
#include <variant>
#include <iostream>

using namespace std;

struct Base {
       Base(variant<double, string> val) : value(val) {}

    void Print() {  //just to ilustrate how it works. Better use ostream
         if (holds_alternative<double>(this->value))
            cout << get<double>(this->value);
         else if (holds_alternative<string>(this->value))
                 cout << get<string>(this->value);
    }

    protected:
        variant<double, string> value;
        variant<double, string> BaseValue() const { return this->value; };
};

struct ChildDouble : public Base {
       ChildDouble(double r) : Base(r) {};

    double Value() const { return get<double>(this->BaseValue()); }
};

struct ChildString : public Base {
       ChildString(string s) : Base(s) {};

    string Value() const { return get<string>(this->BaseValue()); };
};

int main() {                                //must return int not void
    vector<shared_ptr<Base>>vec;
    vec.emplace_back(new ChildDouble(3.0));
    vec.emplace_back(new ChildString("Thomas"));

    unsigned counter = 0;
    for (const auto& e : vec) {
        cout << "Entry " << counter << " : "; e->Print(); cout << endl;
        ++counter;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM