C++ 中的集合(异构类型的数组)
Sets (arrays of heterogeneous types) in C++
我想用 C++ 模拟 python 集合。
根据 faq,一种惯用的方法是使用 std::vector
将指针(如果是智能指针则更好)存储到句柄 class,然后由特定类型重载我们想将其存储在模拟集中。
这是我的尝试:
// experimenting with vectors of heterogeneous elements (like sets in python)
#include <iostream>
#include <memory>
#include <string>
#include <vector>
// Handle can wrap an int, a double, or a string.
class Handle {
public:
Handle();
virtual int val();
virtual double val();
virtual std::string val();
};
class Int : public Handle {
public:
Int(const int n) : value(n) {};
int val() override { return value; };
private:
int value;
};
class Double : public Handle {
public:
Double(const double n) : value(n) {};
double val() override { return value; };
private:
double value;
};
class String : public Handle {
public:
String(const std::string& n) : value(n) {};
std::string val() override { return value; };
private:
std::string value;
};
int main() {
// v simulates a set with 12 elements of types int, double, or string.
std::vector<std::shared_ptr<Handle>> v(12);
for (int i = 0; i < v.size(); i += 3) {
v[i] = std::shared_ptr<Int>(new Int(i));
v[i + 1] = std::shared_ptr<Double>(new Double(i + 1.));
v[i + 2] = std::shared_ptr<String>(new String(std::to_string(i + 2)));
}
for (auto& it : v) {
std::cout << it->val() << std::endl;
}
}
这种方法比将 v
声明为 std::vector<Handle*>
更可取,因为使用智能指针可以让我忘记手动删除指针,同时实现运行时多态性。
但是上面的代码无法编译,因为我们不允许仅在 return 类型上重载函数(在本例中为 val()
)。
有没有办法修复我的代码,以便我可以模拟具有不同类型的集合?有没有更好的惯用方法?
编辑
这是我在 n 之后更改代码的方式。 'pronouns' m.的回答。
该代码似乎有效,我很高兴将其作为使用继承和智能指针的练习(即使它不是手头特定问题的最佳解决方案,请参阅 Maxim Egorushkin 的回答以获得更好的方法)。
// experimenting with vectors of heterogeneous elements (like sets in python)
#include <iostream>
#include <memory>
#include <string>
#include <vector>
// Handle can wrap an int, a double, or a string.
class Handle {
public:
Handle() {};
virtual void print(std::ostream&) = 0;
};
class Int : public Handle {
public:
Int(const int n) : value(n) {};
void print(std::ostream& s) override { s << value << std::endl; };
private:
int value;
};
class Double : public Handle {
public:
Double(const double n) : value(n) {};
void print(std::ostream& s) override { s << value << std::endl; };
private:
double value;
};
class String : public Handle {
public:
String(const std::string& n) : value(n) {};
void print(std::ostream& s) override { s << value << std::endl; };
private:
std::string value;
};
int main() {
std::vector<std::shared_ptr<Handle>> v(12);
for (size_t i = 0; i < v.size(); i += 3) {
v[i] = std::shared_ptr<Int>(new Int(i));
v[i + 1] = std::shared_ptr<Double>(new Double(i + 1.));
v[i + 2] = std::shared_ptr<String>(new String(std::to_string(i + 2)));
}
for (const auto& it : v) {
it->print(std::cout);
}
}
I would like to simulate python sets in C++. According to the faq, an idiomatic way to do this would be to use an std::vector
which stores pointers...
C++ 中的集合是 std::set
(often a red-black tree) and std::unordered_set
(散列 table)。 std::vector
是一个可调整大小的数组。
如果您知道要存储在集合中的所有类型,请使用 std::variant
作为值,而不是指针。
例如:
#include <unordered_set>
#include <variant>
#include <string>
int main() {
using Value = std::variant<int, double, std::string>;
using ValueSet = std::unordered_set<Value>;
ValueSet s;
s.insert(1); // int
s.insert(3.14); // double
s.insert("hello"); // string
for(auto const& value : s)
visit([](auto&& v) { std::cout << v << '\n'; }, value);
}
C++ 是一种静态类型 语言。每个表达式都有一个静态确定的类型。这意味着您、tge 编译器和其他所有人都能够仅从程序文本中确定类型,而无需尝试执行任何代码。
(由 tge 表达式表示的对象也可能具有动态类型,在 运行 时确定,但这不是我们关心的)。 A
让我们看看这个表达式。
it->val()
它的类型是什么?
唯一可能的答案是 std::string
。的确,it
的类型是std::shared_ptr<Handle>
,Handle::val
指定为returnstd::string
。 Handle
具有派生类型这一事实没有任何作用。无论指向对象的 dynamic 类型是什么,it->val()
的 static 类型都是 std::string
,如果某物具有 std::string
的静态类型,它不能具有 double
的动态类型。这就是为什么您不能以这种方式覆盖 val
的原因。
那么我们要根据对象的动态类型打印不同的东西怎么办呢?
标准的 OOP 解决方案是使用 打印 的方法,而不是 return 打印东西的方法。在 C++ 术语中,这将是一个虚函数,例如
virtual void print() = 0;
在每个派生中覆盖 class。
当然现实生活没有那么简单。如果我们需要打印到任意流怎么办?我们需要将流作为参数传递给 print
。也许我们还需要 to.pass 一些东西。也许我们想要指定打印字段宽度、精度或其他任何内容。如果我们想做一些除了打印之外的其他事情怎么办?我们是否需要为我们想做的每件事都使用一个单独的方法? OOP 研究这些和更多问题的答案,但我不能在一个单一的 SO post 中回答它们 :(
我想用 C++ 模拟 python 集合。
根据 faq,一种惯用的方法是使用 std::vector
将指针(如果是智能指针则更好)存储到句柄 class,然后由特定类型重载我们想将其存储在模拟集中。
这是我的尝试:
// experimenting with vectors of heterogeneous elements (like sets in python)
#include <iostream>
#include <memory>
#include <string>
#include <vector>
// Handle can wrap an int, a double, or a string.
class Handle {
public:
Handle();
virtual int val();
virtual double val();
virtual std::string val();
};
class Int : public Handle {
public:
Int(const int n) : value(n) {};
int val() override { return value; };
private:
int value;
};
class Double : public Handle {
public:
Double(const double n) : value(n) {};
double val() override { return value; };
private:
double value;
};
class String : public Handle {
public:
String(const std::string& n) : value(n) {};
std::string val() override { return value; };
private:
std::string value;
};
int main() {
// v simulates a set with 12 elements of types int, double, or string.
std::vector<std::shared_ptr<Handle>> v(12);
for (int i = 0; i < v.size(); i += 3) {
v[i] = std::shared_ptr<Int>(new Int(i));
v[i + 1] = std::shared_ptr<Double>(new Double(i + 1.));
v[i + 2] = std::shared_ptr<String>(new String(std::to_string(i + 2)));
}
for (auto& it : v) {
std::cout << it->val() << std::endl;
}
}
这种方法比将 v
声明为 std::vector<Handle*>
更可取,因为使用智能指针可以让我忘记手动删除指针,同时实现运行时多态性。
但是上面的代码无法编译,因为我们不允许仅在 return 类型上重载函数(在本例中为 val()
)。
有没有办法修复我的代码,以便我可以模拟具有不同类型的集合?有没有更好的惯用方法?
编辑
这是我在 n 之后更改代码的方式。 'pronouns' m.的回答。 该代码似乎有效,我很高兴将其作为使用继承和智能指针的练习(即使它不是手头特定问题的最佳解决方案,请参阅 Maxim Egorushkin 的回答以获得更好的方法)。
// experimenting with vectors of heterogeneous elements (like sets in python)
#include <iostream>
#include <memory>
#include <string>
#include <vector>
// Handle can wrap an int, a double, or a string.
class Handle {
public:
Handle() {};
virtual void print(std::ostream&) = 0;
};
class Int : public Handle {
public:
Int(const int n) : value(n) {};
void print(std::ostream& s) override { s << value << std::endl; };
private:
int value;
};
class Double : public Handle {
public:
Double(const double n) : value(n) {};
void print(std::ostream& s) override { s << value << std::endl; };
private:
double value;
};
class String : public Handle {
public:
String(const std::string& n) : value(n) {};
void print(std::ostream& s) override { s << value << std::endl; };
private:
std::string value;
};
int main() {
std::vector<std::shared_ptr<Handle>> v(12);
for (size_t i = 0; i < v.size(); i += 3) {
v[i] = std::shared_ptr<Int>(new Int(i));
v[i + 1] = std::shared_ptr<Double>(new Double(i + 1.));
v[i + 2] = std::shared_ptr<String>(new String(std::to_string(i + 2)));
}
for (const auto& it : v) {
it->print(std::cout);
}
}
I would like to simulate python sets in C++. According to the faq, an idiomatic way to do this would be to use an
std::vector
which stores pointers...
C++ 中的集合是 std::set
(often a red-black tree) and std::unordered_set
(散列 table)。 std::vector
是一个可调整大小的数组。
如果您知道要存储在集合中的所有类型,请使用 std::variant
作为值,而不是指针。
例如:
#include <unordered_set>
#include <variant>
#include <string>
int main() {
using Value = std::variant<int, double, std::string>;
using ValueSet = std::unordered_set<Value>;
ValueSet s;
s.insert(1); // int
s.insert(3.14); // double
s.insert("hello"); // string
for(auto const& value : s)
visit([](auto&& v) { std::cout << v << '\n'; }, value);
}
C++ 是一种静态类型 语言。每个表达式都有一个静态确定的类型。这意味着您、tge 编译器和其他所有人都能够仅从程序文本中确定类型,而无需尝试执行任何代码。
(由 tge 表达式表示的对象也可能具有动态类型,在 运行 时确定,但这不是我们关心的)。 A
让我们看看这个表达式。
it->val()
它的类型是什么?
唯一可能的答案是 std::string
。的确,it
的类型是std::shared_ptr<Handle>
,Handle::val
指定为returnstd::string
。 Handle
具有派生类型这一事实没有任何作用。无论指向对象的 dynamic 类型是什么,it->val()
的 static 类型都是 std::string
,如果某物具有 std::string
的静态类型,它不能具有 double
的动态类型。这就是为什么您不能以这种方式覆盖 val
的原因。
那么我们要根据对象的动态类型打印不同的东西怎么办呢?
标准的 OOP 解决方案是使用 打印 的方法,而不是 return 打印东西的方法。在 C++ 术语中,这将是一个虚函数,例如
virtual void print() = 0;
在每个派生中覆盖 class。
当然现实生活没有那么简单。如果我们需要打印到任意流怎么办?我们需要将流作为参数传递给 print
。也许我们还需要 to.pass 一些东西。也许我们想要指定打印字段宽度、精度或其他任何内容。如果我们想做一些除了打印之外的其他事情怎么办?我们是否需要为我们想做的每件事都使用一个单独的方法? OOP 研究这些和更多问题的答案,但我不能在一个单一的 SO post 中回答它们 :(