问题:我有一个带有两个构造函数的不可复制对象。我需要使用其中一个构造函数创建一个对象,然后在一些常用代码中使用它: -
使用可复制对象,它看起来像这样,并且很简单:
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();
这感觉太复杂了。有没有更好的解决方案?
答案 0 :(得分:4)
这是一个非常可怕的黑客攻击,假设Object
是默认构造的:
Object a;
a.~Object();
if (condition) { ::new (&a) Object(p1); }
else { ::new (&a) Object(p2, p3, p4); }
另一种选择是使用union,但你也需要在该设置中手动调用析构函数。
使用Boost.Optional(使用in-place factories)可以实现更清洁的解决方案。 (感谢@ 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" ); }
答案 1 :(得分:3)
对我而言看起来非常合理。它清晰,简单,相对简洁。
答案 2 :(得分:1)
我没有看到复杂性 ...如果你需要根据if条件有效地构建声明指针并使用new是你唯一的选择。您不一定需要做的是:
编辑:在第二句中加入“有效”。
答案 3 :(得分: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
(阅读:动态分配)是您唯一的选择。
那是错的。
答案 4 :(得分:1)
你的解决方案确实没有问题,尽管如此 其他人已经提过,如果你这样会更容易理解 使用条件运算符而不是if。但你应该 考虑重构的可能性。如果你考虑所有 将对象用于单独函数的代码 (通过引用获取对象),然后是:
if ( condition ) {
Object a( p1 );
doWhatever( a );
} else {
Object a( p2, p3, p4 );
doWhatever( a );
}
可能更好(或者不是 - 我认为没有 关于在这两者之间进行选择的“正确”答案。)
答案 5 :(得分:0)
我认为您的代码没问题。
您可能想要考虑条件运算符 ? :
boost::scoped_ptr<Object> a( condition ? new Object(p1) : new Object(p2,p3,p4) );
a->doSomething();
答案 6 :(得分:0)
所以,这是一个快速的技巧,使这项工作,而无需手动构建对象。相反,我创建了一个Deferred<T>
模板,该模板表示自动存储中的对象,其构造被延迟(并且可能永远不会发生)。
buff
中的Deferred
应该替换为union
,因为这会处理对齐问题(假设您有C ++ 11支持的功能)。当你致电constructed
时,get()
为真的断言可能是一个好主意。
构建对象后,您可以隐式将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);
}