开发者

Storing values of any type as type initially supplied in templated factory method in C++?

This is a slightly different question to this one ([Accessing a method from a templated derived class without using virtual functions in c++?) which I asked recently.

I would like to create an instance of an object using a templated factory method, then from then on only be able to set and get a value based on the initial开发者_C百科 type supplied to the item.

Here's a quick example of what I'm trying to achieve:

boost::shared_ptr<Item> item = Item::create<float>();

item->setValue(5);                  // conversion to 5.0 in setting the value
float value = item->value();        // returned value = 5.0

Essentially the only time setValue will fail is if there isn't an implicit conversion from whatever has been supplied to the initial 'type' of the internal value in Item.

So if I made the whole Item class a templated class taking a type, I would need to provide the type of the value every time I created a shared pointer to the Item, which I don't really care about.

The next approach I took was to try and store the initial type in the class and use boost::any for the internal storage, casting the internal type to the initial type specified if there was an implicit conversion. However, I stuggled to store and compare the type information, initially looking at std::type_info, but as setValue took a boost::any, had no way of comparing what was actually passed.

(A slight extension to this might be providing a variant-style list of options to the template argument in the creation and returning the value in the native supplied type.)

There may be a design pattern I'm not aware of, or a different approach I haven't considered, so I'd be interested in hearing any suggestions of how to tackle this?


Consider this:

boost::shared_ptr<Item> item1 = Item::create<float>();
boost::shared_ptr<Item> item2 = Item::create<string>();

item1 and item2 have the same (static) type: boost::shared_ptr. So there isn't any way you can make this compile:

item2.setValue("foo");

and have this fail to compile:

item1.setValue("foo");

I'm not sure what you're saying about "if I made the whole Item class a templated class taking a type, I would need to define this every time I call setValue or value (setValue<float>(5))". It sounds like you're actually talking about making each method templated, not the whole class. If the Item class was templated then you'd write:

boost::shared_ptr<Item<float> > item = Item<float>::create();

and then you can easily have the setValue() method accept only the matching type.


Does this work for you?

#include <iostream>
#include <boost/shared_ptr.hpp>

using namespace std;
using namespace boost;

template<typename T> class Item
{
    Item(); // prohibited
    Item (const T & t) : m_value(t) {}
public :
    static shared_ptr<Item<T>> Create(const T & t) 
    { return shared_ptr<Item<T>>(new Item<T>(t)); }
    const T & getValue() const { return m_value; }
    void setValue(const T & v) { m_value = v; }
private :
    T m_value;
};

template<typename T> class Factory
{
public :
    static shared_ptr<Item<T>> CreateItem(const T & value)
    { return Item<T>::Create(value); }
};

void main()
{
    shared_ptr<Item<int>> a = Factory<int>::CreateItem(5);
    shared_ptr<Item<float>> b = Factory<float>::CreateItem(6.2f);
    std::cout << a->getValue() << std::endl;
    std::cout << b->getValue() << std::endl;
    a->setValue(3);
    b->setValue(10.7f);
    std::cout << a->getValue() << std::endl;
    std::cout << b->getValue() << std::endl;
}
  • edit - enforced more const-ness nazism.


Your problem with boost::any is that it's too flexible. The basic idea behind it is still sane. What you need to do is wrap the initial value in an object of type template<typename T> class ItemOfType<T> : public Item. Item::create<T> would then return such a typed object. When you later try to assign a U, you first test if you can dynamic_cast your this to ItemOfType<U>*. This covers your T==U case.

The harder part is the question whether a U is assignable to a T. The usual solution is to instantiate templates, but in your create<T> you don't have the U and in your Item::operator=(U const) you don't have the original T.


This sounds very similar to boost.any. Have you look at this already?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜