基于策略的模板设计:如何访问类的某些策略?

发布于 2024-08-24 03:05:14 字数 1904 浏览 8 评论 0原文

我有一个类使用多个模板化策略。在以下示例中,它称为 Dish。我将许多这些 Dish 存储在 vector 中(使用指向简单基类的指针),但随后我想提取并使用它们。但我不知道它们的具体类型。

这是代码;它有点长,但非常简单:

#include <iostream>
#include <vector>

struct DishBase {
  int id;
  DishBase(int i) : id(i) {}
};

std::ostream& operator<<(std::ostream& out, const DishBase& d) {
  out << d.id;
  return out;
}

// Policy-based class:
template<class Appetizer, class Main, class Dessert>
class Dish : public DishBase {
  Appetizer appetizer_;
  Main main_;
  Dessert dessert_;
public:
  Dish(int id) : DishBase(id) {}
  const Appetizer& get_appetizer() { return appetizer_; }
  const Main& get_main() { return main_; }
  const Dessert& get_dessert() { return dessert_; }
};

struct Storage {
  typedef DishBase* value_type;
  typedef std::vector<value_type> Container;
  typedef Container::const_iterator const_iterator;
  Container container;
  Storage() {
    container.push_back(new Dish<int,double,float>(0));
    container.push_back(new Dish<double,int,double>(1));
    container.push_back(new Dish<int,int,int>(2));
  }
  ~Storage() {
    // delete objects
  }
  const_iterator begin() { return container.begin(); }
  const_iterator end() { return container.end(); }  
};

int main() {
  Storage s;
  for(Storage::const_iterator it = s.begin(); it != s.end(); ++it){
    std::cout << **it << std::endl;
    std::cout << "Dessert: " << *it->get_dessert() << std::endl; // ??
  }
  return 0;
}

棘手的部分就在这里,在 main() 函数中:

    std::cout << "Dessert: " << *it->get_dessert() << std::endl; // ??

我如何访问甜点?我什至不知道甜点类型(它是模板化的),更不用说我从存储中获取的对象的完整类型了。

这只是一个玩具示例,但我认为我的代码可以简化为这样。我只想传递这些 Dish 类,代码的不同部分将访问它的不同部分(在示例中:它的开胃菜、主菜或甜点)。

I have a class that uses several policies that are templated. It is called Dish in the following example. I store many of these Dishes in a vector (using a pointer to simple base class), but then I'd like to extract and use them. But I don't know their exact types.

Here is the code; it's a bit long, but really simple:

#include <iostream>
#include <vector>

struct DishBase {
  int id;
  DishBase(int i) : id(i) {}
};

std::ostream& operator<<(std::ostream& out, const DishBase& d) {
  out << d.id;
  return out;
}

// Policy-based class:
template<class Appetizer, class Main, class Dessert>
class Dish : public DishBase {
  Appetizer appetizer_;
  Main main_;
  Dessert dessert_;
public:
  Dish(int id) : DishBase(id) {}
  const Appetizer& get_appetizer() { return appetizer_; }
  const Main& get_main() { return main_; }
  const Dessert& get_dessert() { return dessert_; }
};

struct Storage {
  typedef DishBase* value_type;
  typedef std::vector<value_type> Container;
  typedef Container::const_iterator const_iterator;
  Container container;
  Storage() {
    container.push_back(new Dish<int,double,float>(0));
    container.push_back(new Dish<double,int,double>(1));
    container.push_back(new Dish<int,int,int>(2));
  }
  ~Storage() {
    // delete objects
  }
  const_iterator begin() { return container.begin(); }
  const_iterator end() { return container.end(); }  
};

int main() {
  Storage s;
  for(Storage::const_iterator it = s.begin(); it != s.end(); ++it){
    std::cout << **it << std::endl;
    std::cout << "Dessert: " << *it->get_dessert() << std::endl; // ??
  }
  return 0;
}

The tricky part is here, in the main() function:

    std::cout << "Dessert: " << *it->get_dessert() << std::endl; // ??

How can I access the dessert? I don't even know the Dessert type (it is templated), let alone the complete type of the object that I'm getting from the storage.

This is just a toy example, but I think my code reduces to this. I'd just like to pass those Dish classes around, and different parts of the code will access different parts of it (in the example: its appetizer, main dish, or dessert).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

凡间太子 2024-08-31 03:05:15

在我看来,你所拥有的并不完全是基于策略的设计……如果是的话,你的班级应该实际实施(即扩展)这些策略。

现在,回到你的问题/例子。在您的容器中,您存储一个“DishBase*”。正确的?从那时起,您将丢失有关集合中对象的实际类型的所有编译时信息。所以,恐怕你尝试做的事情被证明是不可能的。

可以做的是使用实际的基于策略的设计,例如。

template<class Appetizer, class Main, class Dessert>
class Dish : public DishBase, Appetizer, Main, Dessert {
}

然后,您可以简单地使用dynamic_cast在运行时检查您是否可以将对象转换为任何具体的开胃菜/甜点/主菜。

但从您的描述中,我得到的印象是您实际上需要抽象基类(即抽象基类可能是对您有意义的设计,而不是策略)。

What you have is not exactly policy-based design IMO... if it were, your class should've actually implemented (i.e. extended) the policies.

Now, back to your question/example. In your container, you store a "DishBase*". Right? From that point on, you loose any compile-time information wrt the actual type of the objects in the collection. So, I'm afraid what you try to do is provably impossible.

What you could do, is use an actual policy-based design, eg.

template<class Appetizer, class Main, class Dessert>
class Dish : public DishBase, Appetizer, Main, Dessert {
}

Then, you could simply use dynamic_cast to check at runtime that you can convert your object to any concrete Appetizer/Dessert/Main.

But from your description, I get the impression that you actually need abstract base classes (i.e. abstract base classes may be the design that makes sense for you, and not policies).

时间你老了 2024-08-31 03:05:15

您将需要有适当的成员函数来进行查询(在本例中是具体 Dessert 类型的重载)。这些政策应该揭示一种发现方式。这是一个简短的例子:

#include <iostream>
using namespace std;

struct TA { virtual string foo() { return "TA::foo\n"; } };
struct DTA  : TA { virtual string foo() { return "DTA::foo\n"; } };

template <class T>
struct C {
    T t;
};

template <class T>
ostream& operator <<(ostream& o, C<T> c) {
    o << c.t.foo();
    return o;
}

int main(int argc, char* argv[]) 
{
    C<DTA> c;
    cout << c;
}

You will need to have appropriate member functions for querying (in this case an overload for the concrete Dessert type). The policies should expose a way of discovery. Here's a short example:

#include <iostream>
using namespace std;

struct TA { virtual string foo() { return "TA::foo\n"; } };
struct DTA  : TA { virtual string foo() { return "DTA::foo\n"; } };

template <class T>
struct C {
    T t;
};

template <class T>
ostream& operator <<(ostream& o, C<T> c) {
    o << c.t.foo();
    return o;
}

int main(int argc, char* argv[]) 
{
    C<DTA> c;
    cout << c;
}
落日海湾 2024-08-31 03:05:15

我的理解是基于策略的模板类对容器不太友好。对于这类事情,我只是选择普通的多态性。不过我对解决方案很感兴趣。

编辑:我在 Alexandrescu 的《Modern C++ Desing》书中找不到任何 stl 容器的用法,这也许不是巧合。

EDIT2:有关多态性和通用性之间摩擦的更多详细信息可以在这里找到 http://www.artima .com/cppsource/type_erasure.html。您的容器也许可以由 boost::any 对象组成?

My understanding is that policy-based template classes are not very container friendly. I just opt for plain old polymorphism for this kind of things. I'd be interested in a solution though.

EDIT: It's perhaps not by coincidence that I cannot find any usage of stl containers throughout Alexandrescu's "Modern C++ Desing" book.

EDIT2: More details on the friction between polymorphism and genericity can be found here http://www.artima.com/cppsource/type_erasure.html. You container can perhaps be made of boost::any objects?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文