5

我正在尝试存储和操作具有不同参数类型的模板类对象列表;模板类有两个参数化方法,一个返回参数类型,一个void接受它作为输入。

更具体地说,我有一个定义如下的模板类:

template<typename T>
class Test
{
public:
    virtual T a() = 0;
    virtual void b(T t) = 0;
};

以及它的不同规格,例如:

class TestInt : public Test<int>
{
public:
    int a() {
        return 1;
    }
    void b(int t) {
        std::cout << t << std::endl;
    }
};

class TestString : public Test<std::string>
{
public:
    std::string a() {
        return "test";
    }
    void b(std::string t) {
        std::cout << t << std::endl;
    }
};

我希望能够将两者的不同对象存储在一个列表中,TestInt然后TestString键入并循环调用一种方法作为另一种方法的输入,如下所示:

for (auto it = list.begin(); it != list.end(); ++it)
    (*it)->b((*it)->a());

我已经调查过了boost::any,但我无法将迭代器强制转换为特定的类,因为我不知道每个存储对象的具体参数类型。也许这不能用静态类型语言如 C++ 来完成,但我想知道是否有办法解决它。

为了完整起见,我要补充一点,我的总体目标是开发一个“参数化观察者”,即能够定义具有不同参数的观察者(与观察者模式一样):Test类是观察者类,而我试图正确定义的不同类型的观察者的列表存储在主题类中,它通过两种方法a()b().

4

2 回答 2

2

虚拟在这里实际上没有任何意义,因为每个T签名都是不同的。

因此,您似乎有另一个版本的永恒“我们如何模拟虚函数模板”或“如何创建没有虚函数的接口”:

第一个基本上包含一个你可以在这里使用的想法。

这是我要做什么的想法:

Live On Coliru

#include <algorithm>
#include <iostream>

namespace mytypes {
    template <typename T>
    struct Test {
        T a() const;
        void b(T t) { std::cout << t << std::endl; } 
    };

    template <> int         Test<int>::a()         const { return 1;      }
    template <> std::string Test<std::string>::a() const { return "test"; }

    using TestInt    = Test<int>;
    using TestString = Test<std::string>;
}

#include <boost/variant.hpp>

namespace mytypes { 
    using Value = boost::variant<int, std::string>;

    namespace detail {
        struct a_f : boost::static_visitor<Value> {
            template <typename T>
            Value operator()(Test<T> const& o) const { return o.a(); }
        };
        struct b_f : boost::static_visitor<> {
            template <typename T>
            void operator()(Test<T>& o, T const& v) const { o.b(v); }

            template <typename T, typename V>
            void operator()(Test<T>&, V const&) const {
                throw std::runtime_error(std::string("type mismatch: ") + __PRETTY_FUNCTION__);
            }
        };
    }

    template <typename O>
    Value a(O const& obj) {
        return boost::apply_visitor(detail::a_f{}, obj);
    }

    template <typename O, typename V>
    void b(O& obj, V const& v) {
        boost::apply_visitor(detail::b_f{}, obj, v);
    }
}

#include <vector>

int main()
{
    using namespace mytypes;
    using AnyTest = boost::variant<TestInt, TestString>;

    std::vector<AnyTest> list{TestInt(), TestString(), TestInt(), TestString()};

    for (auto it = list.begin(); it != list.end(); ++it)
        b(*it, a(*it));
}

这打印

1
test
1
test

奖励积分

如果您坚持,您可以将AnyTest变体包装到适当的类中,并a()b(...)其上具有成员函数:

Live On Coliru

int main()
{
    using namespace mytypes;
    std::vector<AnyTest> list{AnyTest(TestInt()), AnyTest(TestString()), AnyTest(TestInt()), AnyTest(TestString())};

    for (auto it = list.begin(); it != list.end(); ++it)
        it->b(it->a());
}
于 2015-06-15T09:05:00.903 回答
2

扩展我上面的评论,我目前能想到的最简单的实现你正在尝试做的事情 - 至少正如我从你的示例代码中理解的那样 - 如下:

/* Interface for your container, better not forget the destructor! */
struct Test {
  virtual void operate(void) = 0;
  virtual ~Test() {}
};
/* Implementation hiding actual type */
template<typename T>
struct TestImpl : public T, public Test {
  void operate(void) {
    T::b(T::a());
  }
};

/* Actual code as template policies */
struct IntTest {
  int a(void) {
    return 42;
  }
  void b(int value) {
    std::cout << value << std::endl;
  }
};
struct StringTest {
  std::string a(void) {
    return "Life? Don't talk to me about life.";
  }
  void b(std::string value) {
    std::cout << value << std::endl;
  }
};

然后,您需要为类对象创建一个容器,Test并用相应的对象填充它TestImpl<IntTest>TestImpl<StringTest>等等。例如,为了避免对象切片,您需要引用或指针语义std::vector<std::unique_ptr<Test> >

for (auto it = list.begin(); it != list.end(); ++it) {
  (*it)->operate();
}
于 2015-06-15T09:55:51.357 回答