亲爱的Stack Exchange专家,
我正在尝试建立一个类(多变量分布函数),它将boost分布存储在std :: vector(边缘分布函数)中。 虽然这可以使用boost :: variant(参见我的问题:Boost: Store Pointers to Distributions in Vector),但我也尝试了boost :: any。 原因是变量我必须在设置变体时对潜在类型(边缘分布)进行硬编码,我想避免这种情况。
虽然不同的已实现的分发类不共享公共父类,但是有一些函数,例如boost :: math :: cdf或boost :: math :: pdf,可以应用于所有发行版,并且我想要在std :: vector上应用迭代。
使用任何我生成下面的代码(运行正常),但现在我遇到了问题,即any_cdf函数需要检查类型。
虽然我在设置向量时避免了对类型进行硬编码(对于变体)我现在需要对any_cdf函数中的类型进行硬编码(而带变体的解决方案可以通过a处理cdf函数的应用)模板化访问者函数,因此没有任何类型规范)这意味着要管理大量代码,很多if语句......
然而,逻辑根本没有改变(我转换类型,然后在所有if语句中应用cdf函数),如果除了boost分配以外的东西存储在中,我真的不会关心函数的行为清单。
那么有没有机会吃我的蛋糕并且吃它,这意味着没有被迫在any_cdf中硬编码分发的类型(很像变体的模板化访问者函数)?
非常感谢你的帮助,H。
P.S。如果这不可行,在这种情况下,我通常会更好地使用boost :: any或boost :: variant吗?
#include <boost/math/distributions.hpp>
#include <boost/any.hpp>
#include <vector>
#include <iostream>
#include <limits>
//template function to apply cdf
template<class T> T any_cdf(boost::any a, T &x){
//declare return value
T y;
//cast any with hardcoded types
if (a.type() == typeid(boost::math::normal_distribution<T>)){
y = boost::math::cdf(boost::any_cast< boost::math::normal_distribution<T> >(a),x);
} else if (a.type() == typeid(boost::math::students_t_distribution<T>)){
y = boost::math::cdf(boost::any_cast< boost::math::students_t_distribution<T> >(a), x);
} else {
//return NaN in case of failure or do something else (throw exception...)
y = std::numeric_limits<T>::quiet_NaN();
}
return(y);
}
int main (int, char*[])
{
//get distribution objects
boost::math::normal_distribution<double> s;
boost::math::students_t_distribution<double> t(1);
//use any to put just any kind of objects in one vector
std::vector<boost::any> vec_any;
vec_any.push_back(s);
vec_any.push_back(t);
//evaluation point and return value
double y;
double x = 1.96;
for (std::vector<boost::any>::const_iterator iter = vec_any.begin(); iter != vec_any.end(); ++iter){
y = any_cdf<double>(*iter,x);
std::cout << y << std::endl;
}
return 0;
}
编辑:关于评论似乎不是最简单/最好的选择。但是,出于完整性原因,访问者喜欢boost :: any的实现,请参阅: visitor pattern for boost::any
答案 0 :(得分:3)
注意有关向量和
boost::any
与boost::variant
的解决方案的讨论,请参阅 my older answer 。
如果你真的不需要动态的发行版矢量 - 但只是想要应用一个静态已知的发行版列表,你可以“放弃”#34;有tuple<>
个。
现在,有了凤凰城和Fusion的一点(好吧,很多)魔力,你可以&#34;只是&#34;将cdf
函数调整为 Lazy Actor :
BOOST_PHOENIX_ADAPT_FUNCTION(double, cdf_, boost::math::cdf, 2)
在这种情况下,等效 extended代码示例缩小为:查看 Live On Coliru
int main()
{
typedef boost::tuple<bm::normal, bm::students_t> Dists;
Dists dists(bm::normal(), bm::students_t(1));
double x = 1.96;
boost::fusion::for_each(dists, std::cout << cdf_(arg1, x) << "\n");
std::cout << "\nComposite (multiplication):\t" << boost::fusion::accumulate(dists, 1.0, arg1 * cdf_(arg2, x));
std::cout << "\nComposite (mean):\t\t" << boost::fusion::accumulate(dists, 0.0, arg1 + cdf_(arg2, x)) / boost::tuples::length<Dists>::value;
}
哇。那个......几乎没有6行代码:)最好的部分是它已经兼容c ++ 03 了。
答案 1 :(得分:2)
更新这是假设向量和
boost::any
与boost::variant
的答案。如果您可以使用tuple<>
see my other answer
您将最终以某种方式对潜在类型进行硬编码。
使用variant,您可以使用visitor来分组和隐藏复杂性:
struct invoke_member_foo : boost::static_visitor<double>
{
template <typename Obj, typename... Args>
double operator()(Obj o, Args const&... a) const {
return o.foo(a...);
}
};
这可以应用于您的变体,如
boost::apply_visitor(invoke_member_foo(), my_variant);
使用boost any,你可以通过无聊和手动方式切换类型:
if (auto dist1 = boost::any_cast<distribution1_t>(&my_any))
dist1->foo();
else if (auto dist2 = boost::any_cast<distribution2_t>(&my_any))
dist2->foo();
else if (auto dist3 = boost::any_cast<distribution3_t>(&my_any))
dist3->foo();
IMO这显然不如可维护性,例如
您无法轻松扩展类型列表,其类型类型足以满足相同的概念并获得支持 - 您需要手动向案例开关添加案例(如果你没有 - 你运气不好,那就没有错误,你会有(沉默的)错误。 variant
你只是每当访问者无法处理您的类型时,都会收到编译错误。
这项工作^(类型切换)对于您希望在整个板上实施的每个操作都会重复。 当然,您可以实现一次类型切换,并将实际实现作为仿函数提供,但此时您已经实现了<{> <{1>的完全等效正如我为变体所展示的那样,除了实现效率低得多。
static_visitor
只能包含boost::any
的值。 Boost CopyConstructible
甚至可以包含引用(例如variant
)并且具有(某些)移动语义支持
简而言之,boost::variant<dist1_t&, dist2_t&>
提前节省了时间,但它只是将工作转移到呼叫站点。
从积极的方面来说,让我与您分享一个我喜欢的习语,这使得访问者可以像普通的免费功能一样访问。让我们为变体重写boost::any
函数:
any_cdf
可以找到完整运行的程序 Live On Coliru
演示列表
namespace detail
{
template <typename T> struct var_cdf_visitor : boost::static_visitor<T> {
template <typename Dist>
T operator()(Dist& dist, T& x) const { return boost::math::cdf(dist, x); }
};
}
template<class T> T var_cdf(VarDist<T> a, T &x)
{
static detail::var_cdf_visitor<T> vis;
return boost::apply_visitor(
boost::bind(vis, ::_1, boost::ref(x)),
a);
}
实际上,虽然我使用了一些c ++ 11,但使用某些c ++ 1y功能(如果你的编译器有这些功能)可以使它变得更漂亮。
最后,你也可以为c ++ 03工作;它只需要比现在更多的时间来投入它。
答案 2 :(得分:2)
怎么样:
int main (int, char*[])
{
boost::math::normal_distribution<double> s;
boost::math::students_t_distribution<double> t(1);
typedef std::vector<boost::function<double (double)> > vec_t;
vec_t vec_func;
vec_func.push_back(boost::bind(boost::math::cdf<double>, boost::ref(s), _1));
vec_func.push_back(boost::bind(boost::math::cdf<double>, boost::ref(t), _1));
//evaluation point and return value
double y;
double x = 1.96;
for (vec_t::const_iterator iter = vec_func.begin(); iter != vec_func.end(); ++iter){
y = (*iter)(x);
std::cout << y << std::endl;
}
return 0;
}
将参数绑定到函数模板可能会很棘手。