
頭文件 <boost/utility/in_place_factory.hpp>
頭文件 <boost/utility/typed_in_place_factory.hpp>
假設我們有一個類
struct X
{
X ( int, std:::string ) ;
} ;
還有一個支持空狀態(也就是說,可以包含 0 個對象)的 X 的容器:
struct C
{
C() : contained_(0) {}
~C() { delete contained_ ; }
X* contained_ ;
} ;
一個容器被設計成支持空狀態一般不需要被包含對象是可缺省構造的,但一般需要它是可拷貝構造的,以此作為初始化存入對象的機制:
struct C
{
C() : contained_(0) {}
C ( X const& v ) : contained_ ( new X(v) ) {}
~C() { delete contained_ ; }
X* contained_ ;
} ;
這裡有一個微妙的問題:因為用來初始化存入對象的機制是拷貝構造,必須存在一個早先構造的從其拷貝的源對象。這個對象可能是臨時的而且除了作為源以外不提供其它目的
void foo()
{
// Temporary object created.
C c( X(123,"hello") ) ;
}
這個問題的一個解決方案是支持被包含對像在存入容器時的直接構造。
在這個方案中,用戶直接為容器提供 X 的構造函數的參數:
struct C
{
C() : contained_(0) {}
C ( X const& v ) : contained_ ( new X(v) ) {}
C ( int a0, std::string a1 ) : contained_ ( new X(a0,a1) ) {}
~C() { delete contained_ ; }
X* contained_ ;
} ;
void foo()
{
// Wrapped object constructed in-place
// No temporary created.
C c(123,"hello") ;
}
很顯然,這個方案不太好看,因為容器必須重複所有從被包含類型重載的構造函數(至少是那些所有被容器直接支持的)。
本庫提出一個框架允許某些容器就地直接構造被包含對像而不需要整套從被包含類型重載的構造函數。它也允許容器取消對被包含類型的可被拷貝構造的需求,因為對象可以就地直接構造,而不需要一個拷貝。
對容器的僅由的需求是它必須提供適當的存儲空間(也就是說,恰當的對齊和大小)。自然,容器一般要支持未初始化存儲空間以避免就地構造覆蓋一個已完整構造的對象(這將是就地構造的目標失敗)。
為了這個目的,框架總共提供兩個類的家族,稱為:InPlaceFactories 和 TypedInPlaceFactories。
基本上,這些類持有一個實際參數的序列和一個使用這些參數就地構造一個對象的方法。這個家族中的每一個成員僅僅是參數類表的數目(和類型)不同。第一個家族持有對象的類型在為此目的提供的方法中進行直接構造,反之,第二個家族在它自己的工廠類中合成類型。
從容器的觀點看,使用框架相當於調用工廠的方法去就地構造對象。從用戶的觀點看,它相當於創建正確的工廠對像來持有參數並將它傳遞給容器。
以下這個簡化的示例展示了基本的思想。一個完整的示例在框架的正式規範中:
struct C
{
template<class InPlaceFactory>
C ( InPlaceFactory const& aFactoty )
:
contained_ ( uninitialized_storage() )
{
aFactory.template apply<X>(contained_);
}
~C()
{
contained_ -> X::~X();
delete[] contained_ ;
}
char* uninitialized_storage() { return new char[sizeof(X)] ; }
char* contained_ ;
} ;
void foo()
{
C c( in_place(123,"hello" ) ;
}
以下是 'in_place_factory' 類家族的第一個成員,以及它的相應的輔助模板函數。家族的其它成員僅僅是模板(以及構造函數)參數的數目和類型有所變化。
namespace boost {
struct in_place_factory_base {} ;
template<class A0>
class in_place_factory : public in_place_factory_base
{
public:
in_place_factory ( A0 const& a0 ) : m_a0(a0) {}
template< class T >
void apply ( void* address ) const
{
new (address) T(m_a0);
}
private:
A0 const& m_a0 ;
} ;
template<class A0>
in_place_factory<A0> in_place ( A0 const& a0 )
{
return in_place_factory<A0>(a0);
}
類似地,以下是 ' typed_in_place_factory ' 類家族的第一個成員,以及它的相應的輔助模板函數。家族的其它成員僅僅是模板(以及構造函數)參數的數目和類型有所變化。
namespace boost {
struct typed_in_place_factory_base {} ;
template<class T, class A0>
class typed_in_place_factory : public typed_in_place_factory_base
{
public:
typed_in_place_factory ( A0 const& a0 ) : m_a0(a0) {}
void apply ( void* address ) const
{
new (address) T(m_a0);
}
private:
A0 const& m_a0 ;
} ;
template<class T, class A0>
typed_in_place_factory<A0> in_place ( A0 const& a0 )
{
return typed_in_place_factory<T,A0>(a0);
}
}
就像你看到的,'in_place_factory' 和 'typed_in_place_factory' 模板類僅僅是指定目標類型的方法不同:在第一個家族中,類型作為一個模板參數傳給 apply 成員函數,而第二個作為工廠類的一部分給出。
當容器持有一個唯一的非多態類型(就像 Boost.Optional 的情況),它知道被包含對象的精確的動態類型並可以將它傳遞給一個(無類型)工廠的 apply() 方法。在這種情況下,最終用戶可以使用一個不需要被構造對象的類型就可以被構造出來的 'in_place_factory' 實例。
然而,如果容器持有異種的或多態的對象(就像 Boost.Variant 的情況),要被構造的對象的動態類型必須有工廠函數自己知道。在這種情況下,最終用戶必須使用一個 'typed_in_place_factory' 來代替。
就像在介紹性的簡化示例中展示的,容器類必須包含「接受這些工廠的一個實例,並傳遞對象的存儲空間到工廠的 apply 方法」的方法。
然而,工廠類的類型不能完全在容器類中指定,因為那樣會使工廠的整體目標「允許容器為被包含對象的構造函數接受一個可變參數列表」失敗。
正確的函數重載必須基於每一個家族中所有類中那個唯一獨特而通用的特性,也就是那個基類。
依靠容器類,你可以使用 'enable_if' 生成正確的重載,或者使用下面的 following dispatch 技術(在 Boost.Optional 類中使用):
struct C
{
C() : contained_(0) {}
C ( X const& v ) : contained_ ( new X(v) ) {}
template<class Expr>
C ( Expr const& expr )
:
contained_ ( uninitialized_storage() )
{
construct(expr,&expr)
}
~C() { delete contained_ ; }
template<class InPlaceFactory>
void construct ( InPlaceFactory const& aFactory, boost::in_place_factory_base* )
{
aFactory.template apply<X>(contained_);
}
template<class TypedInPlaceFactory>
void construct ( TypedInPlaceFactory const& aFactory, boost::typed_in_place_factory_base* )
{
aFactory.apply(contained_);
}
X* uninitialized_storage() { return static_cast<X*>(new char[sizeof(X)]) ; }
X* contained_ ;
} ;
最終用戶傳給容器一個工廠對象的實例,它持有直接在容器內部構造被包含對像所需要的實際參數。為了這一點,可以使用輔助模板函數 'in_place'。
調用 'in_place(a0,a1,a2,...,an)' 用給定的參數列表構造一個(無類型)'in_place_factory' 實例。
調用 'in_place
void foo()
{
C a( in_place(123,"hello") ) ; // in_place_factory passed
C b( in_place<X>(456,"world") ) ; // typed_in_place_factory passed
}
Revised September 17, 2004
Copyright Fernando Luis Cacciola Carballal, 2004
Use, modification, and distribution are subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at www.boost.org/LICENSE_1_0.txt)
Developed by Fernando Cacciola, the latest version of this file can be found at www.boost.org, and the boost discussion lists