在具有不同模板参数的列表项上调用参数化方法
Calling parametrised method on list items with different template parameters
我正在尝试存储和操作具有不同参数类型的模板 class 对象列表;模板 class 有两个参数化方法,一个返回参数类型,一个 void 一个接受它作为输入。
更具体地说,我有一个模板 class 定义如下:
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
,但我无法将迭代器转换为特定的 class,因为我不知道每个存储对象的特定参数类型。也许这不能用像 C++ 这样的静态类型语言来完成,但我想知道是否有办法解决它。
为了完整起见,我要补充一点,我的总体目标是开发一个 "parametrised observer",即能够定义一个具有不同参数的观察者(与观察者模式一样):Test
class 是观察者 class,而我试图正确定义的不同类型观察者的列表存储在主题 class 中,它通过a()
和 b()
.
两种方法
虚拟在这里实际上没有任何意义,因为每个 T
签名都是不同的。
看来你还有另一个版本的永恒 "how can we emulate virtual functions templates" 或 "how to create an interface without virtual functions":
- Generating an interface without virtual functions?
- How to achieve "virtual template function" in C++
第一个基本上包含了您可以在这里采用的想法。
这是我要做什么的想法:
#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
变体包装到适当的 class 中,并在其上添加 a()
和 b(...)
成员函数:
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());
}
扩展我上面的评论,我目前能想到的最简单的实现你想要做的事情 - 至少我从你的示例代码中理解它 - 如下:
/* 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;
}
};
然后您需要为 class Test
的对象创建一个容器,并用相应的 TestImpl<IntTest>
、TestImpl<StringTest>
等的对象填充它。为了避免对象切片,您需要引用或指针语义,例如 std::vector<std::unique_ptr<Test> >
。
for (auto it = list.begin(); it != list.end(); ++it) {
(*it)->operate();
}
我正在尝试存储和操作具有不同参数类型的模板 class 对象列表;模板 class 有两个参数化方法,一个返回参数类型,一个 void 一个接受它作为输入。
更具体地说,我有一个模板 class 定义如下:
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
,但我无法将迭代器转换为特定的 class,因为我不知道每个存储对象的特定参数类型。也许这不能用像 C++ 这样的静态类型语言来完成,但我想知道是否有办法解决它。
为了完整起见,我要补充一点,我的总体目标是开发一个 "parametrised observer",即能够定义一个具有不同参数的观察者(与观察者模式一样):Test
class 是观察者 class,而我试图正确定义的不同类型观察者的列表存储在主题 class 中,它通过a()
和 b()
.
虚拟在这里实际上没有任何意义,因为每个 T
签名都是不同的。
看来你还有另一个版本的永恒 "how can we emulate virtual functions templates" 或 "how to create an interface without virtual functions":
- Generating an interface without virtual functions?
- How to achieve "virtual template function" in C++
第一个基本上包含了您可以在这里采用的想法。
这是我要做什么的想法:
#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
变体包装到适当的 class 中,并在其上添加 a()
和 b(...)
成员函数:
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());
}
扩展我上面的评论,我目前能想到的最简单的实现你想要做的事情 - 至少我从你的示例代码中理解它 - 如下:
/* 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;
}
};
然后您需要为 class Test
的对象创建一个容器,并用相应的 TestImpl<IntTest>
、TestImpl<StringTest>
等的对象填充它。为了避免对象切片,您需要引用或指针语义,例如 std::vector<std::unique_ptr<Test> >
。
for (auto it = list.begin(); it != list.end(); ++it) {
(*it)->operate();
}