选择不可复制对象的构造函数

时间:2014-05-29 12:11:04

标签: c++ boost noncopyable multiple-constructors

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

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

6 个答案:

答案 0 :(得分:2)

您可以在堆栈上使用C ++ 11,而无需放置新的并且没有可用的复制/移动构造函数/赋值。观察:

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

仿函数和Foo实例将在堆栈上幸福地生活,没有分配(可能除了在Foo的构造函数中复制字符串)。当Foo超出范围时,Foo会调用它的析构函数。取胜。

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

编辑:或者,这可以做得更短,但更多一点&#34;神奇&#34;:

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&,这将是非常有限的这里。

不是说你应该这样处理它,但它是可能的。

答案 1 :(得分:1)

你的设计存在缺陷。 &#39; Foo&#39;将类型elision委托给类的用户。看看boost :: any(http://www.boost.org/doc/libs/1_55_0/doc/html/any.html

答案 2 :(得分:1)

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

请参阅here以获取简短示例。

boost::optional对你来说也可能是一个可接受的选项(它基本上只是在内部为你做这件事,以及跟踪对象是否已被初始化)。就optional imo。

而言,就地构造有点混乱

答案 3 :(得分: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();

答案 4 :(得分:0)

使用C ++ 11,您可以:

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

通过使用r值引用,可以避免使用常量,并且仍然可以延长临时值。

答案 5 :(得分:0)

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

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

f.doSomethingComplicated(...);