1. #include <memory>
  2. class AnyWrapper {
  3. public:
  4. virtual void printf(std::ostream &out) = 0;
  5. virtual ~AnyWrapper() = default;
  6. };
  7. template<class T>
  8. class AnyStorage : AnyWrapper {
  9. public:
  10. explicit AnyStorage(const T &obj) :obj(obj) {}
  11. void printf(std::ostream &out) override {
  12. out << obj;
  13. }
  14. private:
  15. T obj;
  16. };
  17. class Any {
  18. private:
  19. std::shared_ptr<AnyWrapper> ptr;
  20. public:
  21. template<class T>
  22. Any(const T &obj) {
  23. ptr.reset((AnyWrapper*)new AnyStorage<T>(obj));
  24. }
  25. friend std::ostream &operator << (std::ostream &out, const Any &any) {
  26. any.ptr -> printf(out);
  27. return out;
  28. }
  29. };
  30. int main() {
  31. Any a(0);
  32. Any b('1');
  33. std::vector<Any> vec;
  34. vec.push_back(1);
  35. vec.push_back(2);
  36. for (const Any &x : vec) {
  37. std::cout << x << std::endl;
  38. }
  39. return 0;
  40. }

Any 类型存储的是由子类 AnyStorage 转成父类 AnyWrapper 类型的指针。首先要明白要存储数据就要进行声明,而声明必须得知道一个具体的类型。这也是为什么实际存储对象的 AnyStorage 需要一个模板参数,而这个模板参数往往是模板推断出来的。

函数指针

三种存储函数的方式,函数指针,对象重载,lambda 表达式。标准库还有兼容上面三种的类型的 std::function。

  1. int triple(int x) {
  2. return 3*x;
  3. }
  4. struct Tripler {
  5. int operator()(int x) {
  6. return 3*x;
  7. }
  8. };
  9. std::function<int (int)> function(triple);
  10. std::function<int (int)> object(Tripler {});
  11. std::function<int (int)> lambda([](int x) {
  12. return 3*x;
  13. });

std::function

  1. int triple(int x) {
  2. return 3*x;
  3. }
  4. struct Tripler {
  5. int operator()(int x) {
  6. return 3*x;
  7. }
  8. };
  9. template<typename Ret, typename ...Args>
  10. class MyFunctionWrapper {
  11. public:
  12. virtual Ret operator() (Args ...args) = 0;
  13. virtual ~MyFunctionWrapper() = default;
  14. };
  15. template<typename T, typename Ret, typename ...Args>
  16. class MyFunctionStorage : MyFunctionWrapper<Ret, Args...> {
  17. public:
  18. Ret operator() (Args ...args) {
  19. return object(std::forward<Args...>(args...));
  20. }
  21. MyFunctionStorage(const T& obj): object(obj) {}
  22. private:
  23. T object;
  24. };
  25. template<class Ret, class ...Args>
  26. class MyFunction {
  27. public:
  28. template<class T>
  29. explicit MyFunction(const T &func) {
  30. ptr.reset((MyFunctionWrapper<Ret, Args...>*)new MyFunctionStorage<T, Ret, Args...>(func));
  31. }
  32. Ret operator() (Args ...args) {
  33. return (*ptr)(std::forward<Args...>(args...));
  34. }
  35. private:
  36. std::unique_ptr<MyFunctionWrapper<Ret, Args...>> ptr;
  37. };
  38. int main() {
  39. MyFunction<int, int> my(triple);
  40. MyFunction<int, int> my1(Tripler{});
  41. MyFunction<int, int> my2([](int x) {return 3*x;});
  42. std::cout << my(3) << std::endl;
  43. std::cout << my1(3) << std::endl;
  44. std::cout << my2(3) << std::endl;
  45. return 0;
  46. }
  • 为什么 MyFunction 需要模板参数?为什么 MyFunction 的构造函数需要模板参数?

上面说到存储数据就要进行声明,声明需要具体的函数类型,构造函数接受实际的函数指针类型,用于 MyFunctionStorage 存储 func 指针(构造函数使用模板来推断),同时由于函数调用符的重载需要用到 class Ret, class …Args 模板参数,所以三个类都需要用到 class Ret, class …Args 这些模板参数。

  • 为什么 MyFunction my(&triple); 中的 triple 需要加 & ?

MyFunction my(triple); 如果是这样传参的话 T 的类型会被推导为函数标签 int (int),这样的话 T object; 就会被视为函数声明,函数是不可以在类里被定义成一个成员的。类可以有数据成员和方法,方法是类本身的而不是一个字段,它们的存储方式不一样。

std::decay_t

要想达到标准库的效果,我们可以使用 std::decay_t 来将函数退化成函数指针,如果是 C 标准的字符数组也会退化成字符指针。

  1. explicit MyFunction(const T &func) {
  2. ptr.reset((MyFunctionWrapper<Ret, Args...>*)new MyFunctionStorage<std::decay_t<T>, Ret, Args...>(func));
  3. }