Boost C++ 库

...世界上最受尊敬和专业设计的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ 编码标准

第一章 Boost.Functional/Factory 1.0

Tobias Schwinger

Glen Fernandes

根据 Boost 软件许可证,版本 1.0 分发。

目录

简要说明
背景
参考
变更
致谢
参考文献

模板 boost::factory 允许您将 new 表达式封装为函数对象,boost::value_factory 封装了不带 new 的构造函数调用。

boost::factory<T*>()(arg1,arg2,arg3)
// same as new T(arg1,arg2,arg3)

boost::value_factory<T>()(arg1,arg2,arg3)
// same as T(arg1,arg2,arg3)

在 C++11 之前,函数对象的实参必须是左值。可以使用 boost::forward_adapterboost::bind 组成一个也接受右值的工厂。在 C++11 或更高版本中,实参可以是左值或右值。

在传统的面向对象编程中,工厂是一个对象,它实现了一个或多个方法的接口,这些方法构造符合已知接口的对象。

// assuming a_concrete_class and another_concrete_class are derived
// from an_abstract_class

struct a_factory {
    virtual an_abstract_class* create() const = 0;
    virtual ~a_factory() { }
};

struct a_concrete_factory
    : a_factory {
    an_abstract_class* create() const {
        return new a_concrete_class();
    }
};

struct another_concrete_factory
    : a_factory {
    an_abstract_class* create() const {
        return new another_concrete_class();
    }
};

// [...]

int main()
{
    boost::ptr_map<std::string, a_factory> factories;

    // [...]

    factories.insert("a_name",
        std::unique_ptr<a_factory>(new a_concrete_factory));
    factories.insert("another_name",
        std::unique_ptr<a_factory>(new another_concrete_factory));

    // [...]

    std::unique_ptr<an_abstract_class> x(factories.at(some_name).create());

    // [...]
}

这种方法有几个缺点。最明显的一个是存在大量的样板代码。换句话说,用太多的代码来表达一个相当简单的意图。我们可以使用模板来去除其中的一些代码,但这种方法仍然缺乏灵活性。

  • 我们可能想要一个工厂,它接受一些参数并将其转发给构造函数,
  • 我们可能想要使用智能指针,
  • 我们可能想要几个成员函数来创建不同类型的对象,
  • 我们可能不一定需要对象的基类多态,
  • 正如我们将看到的,我们根本不需要工厂基类,
  • 我们可能只想调用构造函数 - 不使用 new 在堆栈上创建对象,并且
  • 最后,我们可能想要使用自定义的内存管理。

经验表明,使用函数对象和通用的 Boost 组件来组合它们,描述回调机制的设计模式(通常需要使用纯面向对象方法编写大量的样板代码)只需几行代码即可实现,而无需额外的类。

工厂是构造函数的回调机制,因此我们提供了两个类模板,boost::value_factoryboost::factory,它们分别通过直接应用构造函数和 new 运算符来封装对象构造。

我们让函数对象将其参数转发到它们封装的构造表达式。在此基础上,boost::factory 可选择使用智能指针和分配器

可以在适当的地方使用编译时多态,

template<class T>
void do_something()
{
    // [...]
    T x = T(a, b);

    // for conceptually similar objects x we neither need virtual
    // functions nor a common base class in this context.
    // [...]
}

现在,为了允许为 T 传入的类型的构造函数使用不均匀的签名,我们可以使用 value_factoryboost::bind 在它们之间进行规范化。

template<class ValueFactory>
void do_something(ValueFactory make_obj = ValueFactory())
{
    // [...]
    typename ValueFactory::result_type x = make_obj(a, b);

    // for conceptually similar objects x we neither need virtual
    // functions nor a common base class in this context.
    // [...]
}

int main()
{
    // [...]

    do_something(boost::value_factory<X>());
    do_something(boost::bind(boost::value_factory<Y>(), _1, 5, _2));
    // construct X(a, b) and Y(a, 5, b), respectively.

    // [...]
}

也许我们希望我们的对象比函数的作用域更长久,在这种情况下,我们必须使用动态分配;

template<class Factory>
whatever do_something(Factory new_obj = Factory())
{
    typename Factory::result_type ptr = new_obj(a, b);

    // again, no common base class or virtual functions needed,
    // we could enforce a polymorphic base by writing e.g.
    //     boost::shared_ptr<base>
    // instead of
    //     typename Factory::result_type
    // above.
    // Note that we are also free to have the type erasure happen
    // somewhere else (e.g. in the constructor of this function's
    // result type).

    // [...]
}

// [... call do_something like above but with boost::factory instead
// of boost::value_factory]

虽然我们可能在前面的例子中创建了多态对象,但我们对工厂使用了编译时多态。如果我们想消除工厂的类型,从而允许在运行时进行多态,我们可以使用 Boost.Function 来实现。第一个例子可以改写如下。

typedef boost::function<an_abstract_class*()> a_factory;

// [...]

int main()
{
    std::map<std::string, a_factory> factories;

    // [...]

    factories["a_name"] = boost::factory<a_concrete_class*>();
    factories["another_name"] = boost::factory<another_concrete_class*>();

    // [...]
}

当然,我们可以很容易地创建接受参数和/或返回 智能指针 的工厂。

描述

调用类型 T 的构造函数的函数对象模板。

头文件

#include <boost/functional/value_factory.hpp>

概要

namespace boost {

template<class T>
class value_factory;

} // boost

符号

T

至少有一个公共构造函数的任意类型

a0...aN

T 的构造函数的实参值

F

类型 value_factory<F>

f

F 的实例对象

表达式语义

表达式

语义

F()

创建类型为 F 的对象。

F(f)

创建类型为 F 的对象。

f(a0...aN)

返回 T(a0...aN)

F::result_type

是类型 T

限制

在 C++11 之前,支持的参数最大数量为 10。从 C++11 开始,支持任意数量的参数。

描述

函数对象模板,为作为模板参数给出的指针类型动态构造一个指针对象。智能指针可以用于模板参数,前提是 pointer_traits<Pointer>::element_type 产生指针类型。

如果给定一个 分配器,则将其用于内存分配,并使用 new 运算符的放置形式来构造对象。调用析构函数并使用分配器的副本释放内存的函数对象用于 Pointer 的第二个构造函数参数(因此它必须是一个提供合适构造函数的 智能指针,例如 boost::shared_ptr)。

如果第三个模板参数是 factory_passes_alloc_to_smart_pointer,则分配器本身将用于 Pointer 的第三个构造函数参数(boost::shared_ptr 然后使用分配器来管理其单独分配的引用计数器的内存)。

头文件

#include <boost/functional/factory.hpp>

概要

namespace boost {

enum factory_alloc_propagation {
    factory_alloc_for_pointee_and_deleter,
    factory_passes_alloc_to_smart_pointer
};

template<class Pointer,
    class Allocator = void,
    factory_alloc_propagation Policy = factory_alloc_for_pointee_and_deleter>
class factory;

} // boost

符号

T

至少有一个公共构造函数的任意类型

P

指向 T 的指针或智能指针

a0...aN

T 的构造函数的实参值

F

类型 factory<P>

f

F 的实例对象

表达式语义

表达式

语义

F()

创建类型为 F 的对象。

F(f)

创建类型为 F 的对象。

f(a0...aN)

使用 a0...aN 作为构造函数调用的参数,动态创建类型为 T 的对象。

F::result_type

是删除顶层 cv 限定符后的类型 P

限制

在 C++11 之前,支持的参数最大数量为 10。从 C++11 开始,支持任意数量的参数。

Boost 1.72.0

Glen Fernandes 重写了 factoryvalue_factory 的实现,以提供以下功能

  • 如果可用,则支持右值参数
  • 如果可用,则通过可变参数模板支持任意数量的参数
  • 支持最终的分配器
  • 支持使用花哨指针的分配器
  • 支持禁用异常(BOOST_NO_EXCEPTIONS
  • 改进了编译时间

以下功能已被删除

  • 通过 BOOST_FUNCTIONAL_VALUE_FACTORY_MAX_ARITY 增加 C++03 编译器的限制
  • 通过 `BOOST_FUNCTIONAL_FACTORY_SUPPORT_NONE_T` 使用 boost::none_t 代替 void

Boost 1.58.0

为了移除对 Boost.Optional 的依赖,分配器的默认参数已从 boost::none_t 更改为 void。如果您有代码因为使用 boost::none_t 而停止工作,一个快速修复方法是定义 BOOST_FUNCTIONAL_FACTORY_SUPPORT_NONE_T,这将恢复支持,但这将在未来的版本中被移除。正确修复这个问题应该相对容易。

感谢 Tobias Schwinger 创建了这个库。

Eric Niebler 请求一个函数来调用类型的构造函数(参数作为元组提供)作为 Fusion 的一个特性。这些工厂工具是这个想法的一个分解概括。

Dave Abrahams 建议使用智能指针来支持异常安全,为实现提供了有用的提示。

Joel de Guzman 的文档风格是从 Fusion 复制的。

感谢 Peter Dimov 分享他对语言细节及其演变的见解。

  1. 设计模式,Gamma 等人 - Addison Wesley 出版社,1995
  2. 标准模板库程序员指南,惠普公司,1994
  3. Boost.Bind,Peter Dimov,2001-2005
  4. Boost.Function,Douglas Gregor,2001-2004