2013年10月11日18:05由Jens Weller发表
C++11中的标准库的某些部分是在Boost中先于标准库的。在玩C++11时,您会习惯于使用标准库中的一些部件,这些部件在C++03中与它们的Boost对应部分一起使用。此外,现在也出现了一些基于C++11的库,因此与Boost或C++11代码的接口很快就成为一个问题。
Boost已经在C++03中使用多年了,所以使用C++11中的Boost版本是很自然的选择,这些版本现在是STD::,以便能够与C++03进行接口。但也有一些人会很乐意使用C++11,并且更喜欢标准库而不是使用Boost。这两种立场在任何程度上都是混合的,没有一个是错误的。尽管如此,越来越多地使用C++11,我开始看到不同之处,并且经常思考如何在“旧”Boost和“新”C++类型之间进行接口。
随着C++的发展,特别是库的特性在一定程度上可以获得Boost。Boost::文件系统是当今已经存在的最明显的库,并且已经通过标准化,很快就成为TS,很可能是C++1Y的一部分。线程已经提供了未来::那么,用于并发的TS也将导致Boost中基于执行器和基于任务的并行化库。虽然C++标准化需要时间,但Boost可以更快地移动,并且更早地实现特性,然后才是标准的特性。实际上,Boost的最后一个版本主要采用了C++11,F.E。线程现在提供了一个类似的(更高级的,如未来::那时)接口,如std::线程。
因此,在这篇博客文章中,我确实查看了Boost::和std::function,以及std::线程/Boost::线程,以便查看Boost模板中使用的概念,比如可锁。请记住,这段代码是用来做测试的,在现实生活中,这将发生在更复杂的代码中,也许对你来说是不可见的。所有测试代码都用GCC/MinGW4.8编译(或不编译)。
功能
一些混合Boost::和STD::Function的测试代码:
void myFunc()
{
std::cout << "myFunc" << std::endl;
}
void bFunc(boost::function<void()> bfunc)
{
bfunc();
}
void stdFunc(std::function<void()> stdfunc)
{
stdfunc();
}
struct foo
{
int i;
foo(int i):i(i){}
void bar(int x){ std::cout << "foo::bar " << i << " " << x <<std::endl;}
};
所以,这是测试设置。我想用这个测试的是我是否可以相互交换类型。很多代码都将Boost::Function用于回调类型,并且我不确定例如,Boost::Function是否除了std::function的一个实例之外。让我们测试:
std::function<void()> stdfunc = myFunc;//std::bind(myFunc);
boost::function<void()> bfunc = myFunc;
bFunc(stdfunc);
stdFunc(bfunc);
foo f(4);
std::function<void()> cstdfunc = std::bind(&foo::bar,&f,23);
boost::function<void()> bstdfunc = boost::bind(&foo::bar,&f,23);
bFunc(cstdfunc);
stdFunc(bstdfunc);
因此,从函数开始,我从一个有点特殊的类型开始。在幕后,它使用类型擦除,以便它可以包装许多不同的东西,你可以调用(函数,绑定F.E.)。这使得上面的代码可以编译,并且可以工作。只有非Const引用(当然)不起作用,因为C++会告诉您实际上您的类型是错误的。显然有一些魔力,这是可行的,如果它有任何好处是另一个问题。该类型将Boost或STD类型封装到一个新实例中,这将导致调用层次中的一个新级别。
“任何好的问题”的答案实际上都是否定的。您应该尽量避免这种情况,因为上面的代码导致了一个新包装类型,每次这样做,都会添加一个新的包装级别。因此,每次您这样做,您添加了一个新的水平的间接你的电话。或者引用STL:
从Boost::Function构建一个STD::Function或者相反的函数是可怕的–您将付出双倍的惩罚。作为一名STL的维护者,这让我想哭得比我所想要的更多。
所以仅仅因为它起作用并不意味着你应该这么做。
智能指针
这里很有趣。例如,Shared_PTR不可能在标准和Boost之间的类型边界上进行接口。另外,UNIQUE_PTR对于标准来说是唯一的,Boost提供了作用域_PTR。标准和Boost中智能指针的版本。是不一样!
一个用于Shared_PTR的简短示例:
std::shared_ptr<foo> std_shared = std::make_shared<foo>(5);
boost::shared_ptr<foo> bshared = std_shared;
我希望你明白,这是不可能的。在这种情况下,最明显的解决方案是依赖TypeT,并让它实现行为,例如,它可以是一个克隆方法。因此,Boost的Shared_PTR可能会占用新副本的新所有权。搬家也许是个有效的策略,但对我来说是一种邪恶的感觉.
.但正如埃里克·尼布勒所指出的推特,有一个两者之间交换指针的解决方案:
template<class T>
boost::shared_ptr<T> make_shared_ptr(const std::shared_ptr<T>& ptr)
{
return boost::shared_ptr<T>(ptr.get(), [ptr](T*){});
}
template<class T>
std::shared_ptr<T> make_shared_ptr(const boost::shared_ptr<T>& ptr)
{
return std::shared_ptr<T>(ptr.get(), [ptr](T*){});
}
此解决方案的优点是,如果所有其他原始副本都被销毁,则删除器中包含的原始Shared_PTR将保持活动状态。因此指针总是被保证是有效的!
同样在Shared_PTR上,Boost::Shared_PTR!=std::Shared_PTR。两个版本共享大部分接口,但添加另一个版本不支持的方法。Std::Shared_PTR已经分配了_Shared和get_删除,这两者都可以添加到Boost中。Boost版本支持数组(因此添加操作符[]),而标准版本只支持自定义删除器。这是有争议的,如果Shared_PTR应该支持数组,因为它实际上不是一个容器,对于一个数组来说,BEGIN()/End()将是很好的选择。
使用UNIQUE_PTR,情况有点不同,它有一个释放方法,它给出了指向调用方的指针的所有权。因此,您可以用一个UNIQUE_PTR在BOOST中初始化一个作用域_PTR,这样就失去了它的所有权。但这是一个单向的解决方案。Scope_PTR永远不会放弃它的所有权,因此,如果您想要传输对象,您必须使用一个克隆方法/复制。用于UNIQUE_PTR的自定义非删除器仅是一个解决方案,如果它的生存期比作用域_ptr短。但是,为什么不坚持推进呢?
元组
我只看了一下元组,因为我不是元组的人,我喜欢std::tie,但是通常不经常使用元组。促进:元组已经存在了一段时间,所以它不太可能在未来遇到它。所以这样的代码会很好:
std::tuple<int,int,int> stdtuple= std::make_tuple(1,2,3);
boost::tuple<int,int,int> btuple = stdtuple;
但是,至少在Boost 1.54的情况下,这是行不通的。同样,让它工作可能不是最好的主意,除非它可以在编译时被完全检查。所以,tuple是一个很好的例子,在Boost和标准类型之间存在着不一致性。但这显然也不是什么大意外。要克服这一差距,您需要编写一些胶合板,或者在代码中添加接受C++11类型的附加接口。
螺纹
让我们混合Boost和std::线程代码,看起来不是个好主意。线程是一个很好的例子,我更喜欢Boost而不是标准。另一个是“regex”,因为它刚刚在10月13日在GCC全面实施。但是,有些代码在模板中,并使用了诸如可锁之类的概念,在我看来,这些概念将允许std::mutex被Boost::lock_Guard锁定。只要所有类型都是模板参数,就可以了。但是一个std::mutex总是分配一个不同的资源,然后Boost::mutex。Boost在这一节中有着明显的优势,它可以并且已经实现了一些非常有用的东西(SharedmutexesF.E.),而C++11并不具备这些功能。因此,在本例中,如果使用Boost::线程,但在我看来,在使用并行性时,可以使用基于任务的解决方案。只有在真正知道自己在做什么的时候,才能编写低级别锁定的代码,并且要非常小心。每次锁定互斥锁时,您都可能会陷入死锁,这仅仅是为了指出低级别线程的问题之一。
结语
该怎么办?没有人适合所有的解决方案,当在您的代码库中使用Boost时,您可能会坚持使用它。Boost类型和标准类型之间的接口通常很棘手。在这种情况下,Boost可以采用并添加支持std::type的构造函数,这是有意义的。在面对这个问题时,用户通常不得不这样做。另一方面,Shared_PTR显示,这将导致代码,其中两个不同的副本可以并行存在。使用概念/接口的通用模板代码可以在一定程度上避免问题,但也只是部分解决方案。
Boost的一个大优点是,在每个相关平台上,它将使用相同的实现(但有时具有不同的后端OFC)。因此,当前进到C++14>1Y>YZ时,Boost可能会提供一些库的早期版本。Boost::可选是另一个很好的例子。
因此,对于代码库,您必须决定要设置哪些规则,要使用哪些版本。将您的代码混合或重构到C++11标准是不可能的,因为C++03在生产中仍在使用多年。当只使用标准中现在也可用的类型时,也可以删除Boost,这将减少一个依赖项。但是Boost提供了如此多的库,这些库并不是任何未来标准的一部分,所以迟早,您可能会想要将Boost再一次引入到您的代码库中。
未来将显示Boost为从C++11标准库类型转换为它的类型提供了多少解决方案。Boost肯定不会放弃它自己的类型,所以这个问题仍然存在,特别是随着C++标准进一步发展到新的水域。