[英]Selecting between two constructors
問題:我有一個帶有兩個構造函數的不可復制對象。 我需要使用構造函數之一創建一個對象,然后在一些通用代碼中使用它:-
使用可復制的對象,它看起來像這樣,並且很容易:
Object a;
if (condition)
a = Object(p1);
else
a = Object(p2,p3,p4);
a.doSomething();
但是,對象是不可復制的,因此我必須這樣做:
boost::scoped_ptr<Object> a;
if (condition)
a = new Object(p1);
else
a = new Object(p2,p3,p4);
a->doSomething();
感覺太復雜了。 有更好的解決方案嗎?
假設Object
是默認可構造的,這是一個非常糟糕的hack:
Object a;
a.~Object();
if (condition) { ::new (&a) Object(p1); }
else { ::new (&a) Object(p2, p3, p4); }
另一個選擇是使用聯合,但是您也需要在該設置中手動調用析構函數。
使用Boost.Optional (使用就地工廠 )可以實現更清潔的解決方案。 (感謝@ K-Ballo挖掘細節!)
#include <boost/optional.hpp>
#include <boost/utility/in_place_factory.hpp>
struct Object
{
explicit Object(int) {}
explicit Object(int, float, std::string) {}
Object(Object const &) = delete;
Object(Object &&) = delete;
Object & operator=(Object const &) = delete;
Object & operator=(Object &&) = delete;
};
boost::optional<Object> a;
if (condition) { a = boost::in_place(0); }
else { a = boost::in_place(0, 1.0f, "two" ); }
就我而言,看起來確實很合理。 清晰,簡單且相對簡潔。
我看不到復雜性 ...如果您需要基於聲明指針的if條件並使用new進行高效構造,則是唯一的選擇。 您不必要做的是:
編輯:在第二句話中添加了“有效”。
auto const doSomethingTo = []( Object&& o ) { o.doSomething(); };
doSomethingTo( condition? Object( p1 ) : Object( p1, p2, p3 ) );
免責聲明:編譯器未觸及的代碼。
編輯 :上面的代碼,當Object( Object&& )
構造函數為private
,無法使用MSVC 11.0進行編譯(甚至是去年11月的CTP也可以),但是可以使用MinGW g ++ 4.7.1和某些版本的clang進行編譯。
看來它應該編譯 。
因此,這可能是Visual C ++中的錯誤-但不幸的是,我沒有找到簡單的解決方法。
對於假定為Visual C ++編譯器的錯誤,存在一個不方便的解決方法:
#include <fstream>
#include <iostream>
using namespace std;
class Object
{
private:
Object( Object const& );
Object( Object&& );
public:
void doSomething() const {}
Object( int ) {}
Object( int, int, int ) {}
};
int main( int argc, char* argv[] )
{
int p1 = 0, p2 = 0, p3 = 0;
bool condition = argc == 2;
auto const doSomething1 = [=]() { Object o( p1 ); o.doSomething(); };
auto const doSomething2 = [=]() { Object o( p1, p2, p3 ); o.doSomething(); };
if( condition ) { doSomething1(); } else { doSomething2(); }
}
另一個答案認為, new
(讀:動態分配)是您唯一的選擇。
錯了
解決方案的確沒有錯,盡管正如其他人已經提到的那樣,如果使用條件運算符而不是if,則更容易理解。 但是您應該考慮重構的可能性。 如果將使用該對象的所有代碼都分解到一個單獨的函數中(通過引用獲取對象),則類似於:
if ( condition ) {
Object a( p1 );
doWhatever( a );
} else {
Object a( p2, p3, p4 );
doWhatever( a );
}
也許是更可取的(或不是-我認為在這兩者之間進行選擇沒有任何“正確”的答案)。
我認為您的代碼還可以。
您可能只想考慮條件運算符 ? :
? :
boost::scoped_ptr<Object> a( condition ? new Object(p1) : new Object(p2,p3,p4) );
a->doSomething();
因此,這是一個快速完成這項工作的技巧,而無需手動構造對象。 相反,我創建了Deferred<T>
模板,該模板表示自動存儲中的一個對象,該對象的構造被推遲(並且可能永遠不會發生)。
Deferred
的buff
應該替換為union
,因為它將處理對齊問題(假設您具有支持該功能的C ++ 11功能)。 調用get()
時constructed
為true可能是個好主意。
構造對象后,可以將Deferred<T>
隱式轉換為T&
,然后將該T&
作為延遲構造T
的別名。
從理論上講,如果可以證明它總是可以構建的,則可以取消constructed
bool
,但我建議您不要這樣做。 除此之外,這應該幾乎與您可以實現的效率一樣。 而且在C ++ 11 union
案例中,它甚至可能符合標准。
哦,是的,應該通過完美的轉發來增強它。
#include <utility>
// does not handle alignment issues:
template<typename T>
struct Deferred {
Deferred():constructed(false) {}
operator T&() { return get(); }
T& get() { return *reinterpret_cast<T*>(&buff[0]); }
template<typename... Args>
T& construct( Args... args ) {
new(&buff[0]) T(args...);
constructed = true;
return get();
}
~Deferred() {
if (constructed) {
get().~T();
}
}
private:
bool constructed;
char buff[sizeof(T)];
};
#include <iostream>
struct Object {
bool is_int;
Object( int x ):is_int(true) {}
Object( double d ):is_int(false) {}
~Object() {
std::cout << "~Object("<<is_int<<") destroyed\n";
}
};
enum which_test {
as_int,
as_double,
do_not,
};
void test(which_test v) {
std::cout << v << "\n";
Deferred<Object> o;
if(v==as_int) {
o.construct( 7 );
} else if (v==as_double) {
o.construct( 7.0 );
} else {
}
}
int main() {
test(as_int);
test(as_double);
test(do_not);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.