在两个构造函数之间进行选择

Selecting between two constructors

本文关键字:之间 行选 选择 构造函数 两个      更新时间:2023-10-16

问题:我有一个带有两个构造函数的不可复制对象。我需要用其中一个构造函数创建一个对象,然后在一些公共代码中使用它:-

有了可复制的对象,它会看起来像这样,而且很容易:

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是默认可构建的:

Object a;
a.~Object();
if (condition) { ::new (&a) Object(p1); }
else           { ::new (&a) Object(p2, p3, p4); }

不要用这个。

另一种选择是使用并集,但您也需要在该设置中手动调用析构函数。


Boost可以实现更清洁的解决方案。可选(使用现场工厂(。(感谢@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" ); }

在我看来非常合理。它清晰、简单且相对简洁。

auto const doSomethingTo = []( Object&& o ) { o.doSomething(); };
doSomethingTo( condition? Object( p1 ) : Object( p1, p2, p3 ) );

免责声明:编译器未触及代码。


EDIT:当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 );
}

可能更可取(或不可取(我认为没有关于在这两者之间选择的"正确"答案(。

我看不到的复杂性。。。如果您需要基于If条件高效地构造,则声明指针并使用new是您唯一的选择。你不一定需要做的是:

  1. 使用scope_ptr(尽管这通常是个好主意(
  2. 在"main"代码中的if中设置构造函数。您的是工厂的典型用例(例如。http://en.wikipedia.org/wiki/Factory_method_pattern)

编辑:在第二句中添加"高效"。

我认为你的代码还可以。

您可能只想考虑条件运算符? :

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为真可能是个好主意。

构造完对象后,可以将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<<") destroyedn";
  }
};
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);
}