在我的一个项目中,我过度使用boost-variant
。在某些时候,我超过了boost-variant
的最大模板参数数(20)。因此,我通过将几个boost-variant
类型链接在一起,如链接列表,推导出以下解决方案。
#include <boost/variant.hpp>
#include <iostream>
template<int T> struct A {
const int value = T;
};
typedef boost::variant<
A<20>,A<21>,A<22>,A<23>,A<24>,A<25>,A<26>,A<27>,A<28>,A<29>,A<30>,A<31>,A<32>,A<33>,A<34>,A<35>,A<36>,A<37>,A<38>,A<39>
> NextVar;
typedef boost::variant<
A<1>,A<2>,A<3>,A<4>,A<5>,A<6>,A<7>,A<8>,A<9>,A<10>,A<11>,A<12>,A<13>,A<14>,A<15>,A<16>,A<17>,A<18>,A<19>,NextVar
> TVar;
struct PrintVisitor : public boost::static_visitor<std::string> {
result_type operator()(const NextVar& n) {
return n.apply_visitor(*this);
}
template<int T>
result_type operator()(const A<T>& a) {
return std::to_string(a.value);
}
};
struct IntVisitor : public boost::static_visitor<int> {
result_type operator()(const NextVar& n) {
return n.apply_visitor(*this);
}
template<int T>
result_type operator()(const A<T>& a) {
return a.value;
}
};
template<int I>
struct AddVisitor : public boost::static_visitor<int> {
result_type operator()(const NextVar& n) {
return n.apply_visitor(*this);
}
template<int T>
result_type operator()(const A<T>& a) {
return a.value+I;
}
};
int main(int argc, char **args) {
TVar x = A<35>();
PrintVisitor v1;
std::cout << x.apply_visitor(v1) << std::endl;
IntVisitor v2;
std::cout << x.apply_visitor(v2) << std::endl;
AddVisitor<10> v3;
std::cout << x.apply_visitor(v3) << std::endl;
}
我真的很惊讶这个解决方法解决了我的问题。还有一粒盐。对于每个访客,我必须包括以下行:
result_type operator()(const NextVar& n) {
return n.apply_visitor(*this);
}
这似乎是一种不必要的代码重复。更糟糕的是,如果我需要boost-variant
中的60种甚至更多类型。我的尝试是为所有访问者定义一个公共基类:
template<typename T>
struct BaseVisitor : public boost::static_visitor<T> {
result_type operator()(const NextVar& n) {
return n.apply_visitor(*this);
}
};
我认为,从BaseVisitor
得到如下所示,可以解决问题:
struct PrintVisitor : public BaseVisitor<std::string> {
template<int T>
result_type operator()(const A<T>& a) {
return std::to_string(a.value);
}
};
但是编译器抱怨:
template-argument for "const A<T> &" could not be derived from "T19"
这类问题可能是最接近的解决方法?
答案 0 :(得分:2)
首先,你可以简单地将此限制增加到20 BOOST_MPL_LIMIT_LIST_SIZE
。
关于你的代码:即使它被编译,BaseVisitor::operator()
也将进行无限递归,因为此时*this
被视为BaseVisitor。
为避免这种情况,您可以使用CRTP代替商品derived()
:
template<class Derived, typename T>
struct BaseVisitor : public boost::static_visitor<T> {
using typename boost::static_visitor<T>::result_type;
Derived & derived() { return static_cast<Derived &>(*this); }
result_type operator()(const NextVar& n) {
return n.apply_visitor( derived() );
}
};
然后将相同的operator()
带入派生类的范围(否则被新的隐藏)(以及模板类所需的result_type
)。
<强> DEMO 强>
如评论中所述,必须在每个派生类中编写别名。为了摆脱它,我们可以在基类中的同一级别收集两个operator()
,并以不同的方式命名派生函数(visit
这里):
template<class Derived, typename T>
struct BaseVisitor : public boost::static_visitor<T> {
using typename boost::static_visitor<T>::result_type;
Derived & derived() { return static_cast<Derived &>(*this); }
result_type operator()(const NextVar& n) {
return n.apply_visitor( derived() );
}
template<int I>
result_type operator()(const A<I>& a) {
return derived().visit(a);
}
};
离开我们:
struct PrintVisitor : public BaseVisitor<PrintVisitor, std::string> {
template<int I>
std::string visit(const A<I>& a) {
return std::to_string(a.value);
}
};
<强> DEMO 强>