类具有相同的接口但参数的类型不同

Rub*_*bik 2 c++ oop polymorphism templates template-meta-programming

我有一个类TypedNode来存储一些数据:

template <typename Type>
class TypedNode {
 public:
  TypedNode() {}
  void SetNodeData(Type data) { data_ = data; }
  Type GetNodeData() { return data_; }

 private:
  Type data_;
};
Run Code Online (Sandbox Code Playgroud)

然后我可以用它:

int main() {
  TypedNode<int> int_node;
  TypedNode<double> double_node;
  TypedNode<Vector3d> vector3_node;

  int_node.SetNodeData(1);
  double_node.SetNodeData(2.3);
  vector3_node.SetNodeData(Vector3d(4,5,6));;
}
Run Code Online (Sandbox Code Playgroud)

但我想定义一个函数来访问:

void Access(std::list<TypedNode> node_list) {
  for (auto node : node_list) {
    node.GetNodeData();
    // if it is an integer, do one thing
    // if it is a double, do another
  }
}
Run Code Online (Sandbox Code Playgroud)

该列表需要一个具体的类,但我需要存储任何类型的节点.

有些我改变了Node的代码:

class NodeBase {
 public:
  NodeBase() {}
};

template <typename Type>
class TypedNode : NodeBase {
 public:
  TypedNode() {}
  void SetNodeData(Type data) { data_ = data; }
  Type GetNodeData() { return data_; }
 private:
  Type data_;
};

void Access(std::list<NodeBase> node_list) {
  for (auto node : node_list) {
    node.GetNodeData();
    // if it is an integer, do one thing
    // if it is a double, do another
  }
}
Run Code Online (Sandbox Code Playgroud)

但是Access()函数只能调用Base类的方法.尽管每个派生类都有一个相同的名称接口SetNodeData,但它们具有不同的类型.所以他们是不同的.它们不能覆盖Base类中的同一个接口.

我能做什么?

================================================== ============

这是我的解决方案:

#include <list>
enum NodeType {
  kInt,
  kDouble,
  kVector3,
};

class NodeBase {
 public:
  NodeBase() {}

  virtual int GetDataInt();
  virtual double GetDataDouble();
  virtual Vector3 GetDataVector3();
  NodeType type() const { return type_; }

 protected:
  void set_type(NodeType type) { type_ = type; }

 private:
  NodeType type_;
};

class NodeInt : NodeBase {
 public:
  NodeInt() { set_type(kInt); }
  int GetDataInt() override { return data_; }
  double GetDataDouble() override { check(false) << "error"; }
  Vector3 GetDataVector3() override { check(false) << "error"; }

 private:
  int data_;
};

class NodeDouble : NodeBase {
 public:
  NodeDouble() { set_type(kDouble); }
  int GetDataInt() override { check(false) << "error"; }
  double GetDataDouble() override { return data_; }
  Vector3 GetDataVector3() override { check(false) << "error"; }

 private:
  double data_;
};

void Access(const std::list<NodeBase>& node_list) {
  for (auto node : node_list) {
    switch (node.type()) {
      case kInt: {
        int data = node.GetDataInt();
        // do something about int
        break;
      }
      case kDouble: {
        double data = node.GetDataDouble();
        // do something about double
        break;
      }
      case kVector3: {
        Vector3 data = node.GetDataVector3();
        // do something about Vector3
        break;
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Öö *_*iib 5

您的TypedNode模板没有明显的价值,它只是一个getter和一个封装数据的setter,所以最好是为了简单起见而消除它.你似乎需要的是一种类型int,double或者Vector3d可以将它们保存在同一个容器中.为此,在C++ 17 中有std :: variant.具有不合标准编译器的人可以使用基本相同的Boost.Variant,也适用于C++ 98.

#include <variant>

struct Vector3d {int x, y, z;};
using Node = std::variant<int,double,Vector3d>;
Run Code Online (Sandbox Code Playgroud)

当然,你可以拥有std::variant<TypedNode<int>,TypedNode<double>,TypedNode<Vector3d>>一些重要的功能.发布TypedNode没有功能,其他但更臃肿的类型.

对于使用相同接口访问变体,有几种方法.例如,它可以使用访问者完成.这里是一个游客NodeOutputostream每个类型的输出Node.

#include <iostream>

struct NodeOutput {
    std::ostream& os_;
    NodeOutput(std::ostream& os) : os_{os} {}

    void operator()(Vector3d const& v3) 
    {
        os_ << "Vector3d (" << v3.x <<", "<< v3.y <<", "<< v3.z <<")\n";
    }
    void operator()(double const& d) {os_ << "Double " << d <<"\n";}
    void operator()(int const& i) {os_ << "Int " << i <<"\n";}
};
Run Code Online (Sandbox Code Playgroud)

使用这样的游客,我们可以写operator<<Node:

std::ostream& operator<< (std::ostream& os, Node const& v) {
    std::visit(NodeOutput{os}, v);
    return os;
}
Run Code Online (Sandbox Code Playgroud)

尝试一下.这std::list是很少使用的容器,所以在这里它被替换std::vector为简单,但它将与其他容器类似地工作.

#include<vector>

int main()
{
    std::vector<Node> nodes;
    nodes.emplace_back(42);
    nodes.emplace_back(6.66);
    nodes.emplace_back(Vector3d{3,2,1});

    for (auto& n: nodes) {std::cout << n;}   
}
Run Code Online (Sandbox Code Playgroud)

输出:

Int 42
Double 6.66
Vector3d (3, 2, 1)
Run Code Online (Sandbox Code Playgroud)