12

假设我有一个具有多个构造函数的不可复制类,如下所示

class Foo: boost::noncopyable
{
  public:
    Foo(std::string s) {...};  // construct one way        
    Foo(int i) {...};  // construct another way
 }

现在,我想构造一个对象,并选择在运行时使用哪个构造函数:

我可以用这样的指针来做到这一点: -

boost::shared_ptr<Foo> f;

if (condition)
  f.reset(new Foo(myString));
else
  f.reset(new Foo(myInteger));

// common code follows
f->doSomethingComplicated(...);

但这感觉混乱而缓慢。有没有一种简单的方法来选择对象的构造函数而不诉诸动态分配?


更多细节:Foo上面的类只是为了说明问题。实际涉及的类是 Windows Gdiplus::Bitmap- http://msdn.microsoft.com/en-gb/library/windows/desktop/ms534420(v=vs.85)。 aspx

4

6 回答 6

2

您可以在堆栈上使用 C++11 来执行此操作,无需放置 new 且没有可用的复制/移动构造函数/赋值。观察:

auto factory = [&]() -> Foo 
{ 
  if (condition) {
    return { myString };
  } else {
    return { myInteger };
  }
};
Foo&& foo = factory();
foo.doSomethingComplicated();

函子和 Foo 实例将愉快地生活在堆栈上,不进行任何分配(可能除了在Foo' 的构造函数中复制字符串)。Foo 超出范围时将调用其析构函数。赢。

使用统一初始化构造返回值时,不涉及复制/移动操作。请注意,返回myString(隐式转换)或Foo(myString)将强制编译器检查对象是否可复制/可移动,即使可以省略此类复制/移动。

编辑:或者,这可以更短,但更“神奇”:

Foo&& foo = [&]() -> Foo 
{ 
  if (condition) {
    return { myString };
  } else {
    return { myInteger };
  }
}();

编辑:Pre-C++11,Visual Studio hacky 解决方案:

当诉诸隐式转换构造函数从函数返回值时,VC 似乎没有检查对象是否可复制。所以这是有效的,即使它违反标准:

Foo make_foo(bool condition, const std::string&s, int i)
{
    if ( condition) {
        return s;
    } else {
        return i;
    }
}

然后就这样使用它:

Foo& f = make_foo(condition, myString, myInteger);

请注意,这是另一个 VC hack,因为根据标准临时不能分配给对可变对象的引用,即它需要更改const Foo&,这在此处将是非常有限的。

并不是说您应该以这种方式处理它,但这是可能的。

于 2014-05-29T13:39:47.780 回答
1

我认为满足您的要求(非动态分配、不可复制、C++ 11 之前)的最佳选择是使用placement new.

请参阅此处以获取简短示例。

boost::optional也可能是您可以接受的选项(它基本上只是在内部为您执行此操作,并跟踪对象是否已初始化)。optional虽然对于imo来说,就地结构有点混乱。

于 2014-05-29T12:42:49.300 回答
1

你的设计有缺陷。'Foo' 将类型省略委托给类的用户。看看 boost::any ( http://www.boost.org/doc/libs/1_55_0/doc/html/any.html )

于 2014-05-29T12:23:39.040 回答
0

如果您只需要构造一个临时对象,我认为您可以使用三元表达式将其绑定到 const 引用:

const Foo &f(condition ? Foo(myString) : Foo(myInteger));

f.doSomethingComplicated(...);
于 2014-05-30T21:57:11.637 回答
0

保持小而简单,我会说。如果这是一个非常局部的问题,为什么不重复调用呢?

if (condition)
{
  Foo f(myString);
  f.doSomethingComplicated();
}
else
{
  Foo f(myInt);
  f.doSomethingComplicated();
}

如果这不可行,请将Foo指针(或智能指针)包装在新类中。

class FooWrapper // copyable
{
private:
    boost::shared_ptr<Foo> m_foo;
public:
    FooWrapper(std::string const &s) : m_foo(new Foo(s)) {}
    FooWrapper(int i) : m_foo(new Foo(i)) {}
    void doSomethingComplicated() { m_foo->doSomethingComplicated(); }
};

FooWrapper foo = condition ? FooWrapper(myString) : FooWrapper(myInt);
foo.doSomethingComplicated();
于 2014-05-29T13:01:23.063 回答
0

使用 C++11,您可以:

Foo&& f = condition ? Foo{myInteger} : Foo{myString};
f.doSomethingComplicated();

通过使用 r 值引用,您可以避免 constness 并仍然延长临时的生命周期。

于 2014-05-29T15:24:58.193 回答