簡體   English   中英

在兩個構造函數之間選擇

[英]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進行高效構造,則是唯一的選擇。 您不必要做的是:

  1. 使用scoped_ptr(盡管通常這是一個好主意)
  2. 在“主”代碼中的if中包含構造函數。 您的工廠是工廠的典型用例(請參見例如http://en.wikipedia.org/wiki/Factory_method_pattern )。

編輯:在第二句話中添加了“有效”。

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>模板,該模板表示自動存儲中的一個對象,該對象的構造被推遲(並且可能永遠不會發生)。

Deferredbuff應該替換為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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM