在两个构造函数之间进行选择
Selecting between two constructors
问题:我有一个带有两个构造函数的不可复制对象。我需要用其中一个构造函数创建一个对象,然后在一些公共代码中使用它:-
有了可复制的对象,它会看起来像这样,而且很容易:
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是您唯一的选择。你不一定需要做的是:
- 使用scope_ptr(尽管这通常是个好主意(
- 在"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);
}
- 如何防止clang格式在流运算符调用之间添加换行符<<
- 引号之间匹配/不匹配,带有不可避免的引号和多行
- 计算所选行的总和
- 通过USB在PC和Arduino之间进行串行通信
- 正则表达式 获取两个换行符之间的文本
- 如果我在下面的代码中使用 list 而不是 vector,为什么在我尝试在迭代器之间执行减法的行中编译失败?
- Arduino上的串行和流之间有什么区别,以及如何实现序列
- 一条线和两行声明之间的差异
- ESP8266和ATMEGA328P之间的串行通信
- 如何从 gtkmm 树视图中获取所选行
- openssl rc4 命令行加密和 cpp 文件实现 rc4 之间的区别
- 如何在多行文本C++的左上角显示复选框
- 如何将所选行的第一列值绑定到变量并在 QT 的 SQL 命令中使用它?
- 嵌入式串行读取操作和桌面PC之间可能有什么区别
- 数组之间的特征行交叉积
- 我的编译行和我的生成文件之间的区别可能导致错误
- 在文件中找到最长的行,签名和未签名的整数表达式之间的比较
- 使用 Qt 获取 csv 文件中两行之间所有行
- c++和arduino之间的串行通信
- 如何在Visual Studio中进行串行端口编程C++Windows和Linux之间的可移植性?