0

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

使用可复制对象,它看起来像这样,并且很容易:

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();

这感觉太复杂了。有没有更好的解决方案?

4

7 回答 7

4

这是一个非常可怕的黑客,假设Object是默认可构造的:

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" ); }
于 2013-01-18T18:09:40.427 回答
3

对我来说看起来完全合理。它清晰,简单且相对简洁。

于 2013-01-18T18:10:41.417 回答
1
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(阅读:动态分配)是您唯一的选择。

那是错误的。

于 2013-01-18T19:57:23.127 回答
1

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

  1. 使用 scoped_ptr (虽然这通常是个好主意)
  2. 在“主”代码中的 if 中有构造函数。您的案例是工厂的典型用例(参见例如http://en.wikipedia.org/wiki/Factory_method_pattern)。

编辑:在第二句中添加了“有效地”。

于 2013-01-18T18:16:20.813 回答
1

您的解决方案确实没有任何问题,尽管正如其他人已经提到的那样,如果您使用条件运算符而不是 if 会更易读。但是你应该考虑重构的可能性。如果您将使用该对象的所有代码分解为一个单独的函数(通过引用获取该对象),则类似于:

if ( condition ) {
    Object a( p1 );
    doWhatever( a );
} else {
    Object a( p2, p3, p4 );
    doWhatever( a );
}

可能更可取(或者不是——我认为在这两者之间进行选择没有任何“正确”的答案)。

于 2013-01-19T01:33:49.603 回答
0

我认为你的代码没问题。

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

boost::scoped_ptr<Object> a( condition ? new Object(p1) : new Object(p2,p3,p4) );
a->doSomething();
于 2013-01-18T18:18:25.427 回答
0

所以,这里有一个快速的技巧来完成这项工作,而无需手动构造对象。相反,我创建了一个Deferred<T>模板来表示自动存储中的一个对象,该对象的构造被延迟(并且可能永远不会发生)。

buffinDeferred应该替换为 a union,因为这将处理对齐问题(假设您有 C++11 特性来支持它)。constructed当你打电话时断言它是正确get()的可能是一个好主意。

一旦你构建了你的对象,你可以隐式地将你的对象Deferred<T>转换为 a T&,然后将其T&用作 deferred-constructed 的别名T

从理论上讲,constructed bool如果你能证明它总是会被构造,你可以取消它,但我建议不要这样做。除此之外,这应该几乎与您可以完成的一样有效。对于 C++11union案例,它甚至可能符合标准。

哦,是的,它应该通过完美的转发来增强。

#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);
}
于 2013-01-21T16:14:03.453 回答