Boost C++ 库

...世界上最受尊敬和专业设计的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu,《C++ Coding Standards

第 1 章。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 之前,函数对象的参数必须是左值 (LValues)。也可以使用 boost::forward_adapterboost::bind 来组合一个也接受右值 (RValues) 的工厂。在 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 产生被指向的类型。

如果给出了 Allocator,则它用于内存分配,并且使用 new 运算符的 placement 形式来构造对象。一个函数对象,它调用析构函数并使用 Allocator 的副本释放内存,用于 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

是类型 P,并移除了顶层 cv 限定符。

限制

在 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. Design Patterns, Gamma et al. - Addison Wesley Publishing, 1995
  2. Standard Template Library Programmer's Guide, Hewlett-Packard Company, 1994
  3. Boost.Bind, Peter Dimov, 2001-2005
  4. Boost.Function, Douglas Gregor, 2001-2004