[英]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 中的聲明值都會讓你的生活更輕松。
另外我建議你:
在帶有更改的代碼下方:
#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.