如何禁止临时工
对于Foo一个class级,有没有办法不给它命名呢?
例如:
Foo("hi");
如果你给它一个名字,只允许它,如下所示?
Foo my_foo("hi");
第一个的生命期只是陈述,第二个是封闭的块。 在我的用例中, Foo
测量构造函数和析构函数之间的时间。 由于我从来没有提到局部variables,所以我经常忘记把它放进去,不小心改变了一生。 我想得到一个编译时错误,而不是。
另一个macros观解决scheme:
#define Foo class Foo
声明Foo("hi");
扩展到class Foo("hi");
,这是不合格的; 但是Foo a("hi")
扩展到class Foo a("hi")
,这是正确的。
这具有与现有(正确)代码的源和二进制兼容的优点。 (这个说法并不完全正确 – 请参阅Johannes Schaub的评论和下面的讨论: “你怎么能知道它是与现有代码的源兼容?他的朋友包括他的头并且具有void f(){int Foo = 0;}而且,每一行定义了Foo类的成员函数都会失败:void class Foo :: bar(){}“ )
如何一点点黑客
class Foo { public: Foo (const char*) {} }; void Foo (float); int main () { Foo ("hello"); // error class Foo a("hi"); // OK return 1; }
使构造函数私有,但给这个类一个创build方法。
这不会导致编译器错误,而是运行时错误。 而不是测量一个错误的时间,你会得到一个可能被接受的例外。
任何你想要防范的构造函数都需要一个默认的参数set(guard)
被调用。
struct Guard { Guard() :guardflagp() { } ~Guard() { assert(guardflagp && "Forgot to call guard?"); *guardflagp = 0; } void *set(Guard const *&guardflag) { if(guardflagp) { *guardflagp = 0; } guardflagp = &guardflag; *guardflagp = this; } private: Guard const **guardflagp; }; class Foo { public: Foo(const char *arg1, Guard &&g = Guard()) :guard() { g.set(guard); } ~Foo() { assert(!guard && "A Foo object cannot be temporary!"); } private: mutable Guard const *guard; };
其特点是:
Foo f() { // OK (no temporary) Foo f1("hello"); // may throw (may introduce a temporary on behalf of the compiler) Foo f2 = "hello"; // may throw (introduces a temporary that may be optimized away Foo f3 = Foo("hello"); // OK (no temporary) Foo f4{"hello"}; // OK (no temporary) Foo f = { "hello" }; // always throws Foo("hello"); // OK (normal copy) return f; // may throw (may introduce a temporary on behalf of the compiler) return "hello"; // OK (initialized temporary lives longer than its initializers) return { "hello" }; } int main() { // OK (it's f that created the temporary in its body) f(); // OK (normal copy) Foo g1(f()); // OK (normal copy) Foo g2 = f(); }
f2
, f3
和"hello"
的返回情况可能不需要。 为了防止投掷,你可以让副本的来源成为临时的,通过重置guard
来保护我们而不是副本的来源。 现在你也明白了为什么我们使用了上面的指针 – 它允许我们变得灵活。
class Foo { public: Foo(const char *arg1, Guard &&g = Guard()) :guard() { g.set(guard); } Foo(Foo &&other) :guard(other.guard) { if(guard) { guard->set(guard); } } Foo(const Foo& other) :guard(other.guard) { if(guard) { guard->set(guard); } } ~Foo() { assert(!guard && "A Foo object cannot be temporary!"); } private: mutable Guard const *guard; };
f2
, f3
和return "hello"
的特性现在总是// OK
。
几年前,我为GNU C ++编译器写了一个补丁 ,为这种情况添加了一个新的警告选项。 这是在Bugzilla项目跟踪。
不幸的是,海湾合作委员会Bugzilla是一个墓地,考虑到补丁包含的functionbuild议去死。 🙂
这是由于希望在代码中使用本地对象作为locking和解锁的小工具,测量执行时间等等,来准确捕捉这个问题的主题。
因为在你的实现中,你不能这样做,但是你可以使用这个规则来获得你的优势:
临时对象不能绑定到非const引用
您可以将代码从类移动到一个独立的函数,它采用非const引用参数。 如果这样做,如果临时尝试绑定到非const引用,则会出现编译器错误。
代码示例
class Foo { public: Foo(const char* ){} friend void InitMethod(Foo& obj); }; void InitMethod(Foo& obj){} int main() { Foo myVar("InitMe"); InitMethod(myVar); //Works InitMethod("InitMe"); //Does not work return 0; }
产量
prog.cpp: In function 'int main()': prog.cpp:13: error: invalid initialization of non-const reference of type 'Foo&' from a temporary of type 'const char*' prog.cpp:7: error: in passing argument 1 of 'void InitMethod(Foo&)'
简单地说,没有默认构造函数,并且需要在每个构造函数中引用一个实例。
#include <iostream> using namespace std; enum SelfRef { selfRef }; struct S { S( SelfRef, S const & ) {} }; int main() { S a( selfRef, a ); }
不,恐怕这是不可能的。 但是你可以通过创build一个macros来获得同样的效果。
#define FOO(x) Foo _foo(x)
有了这个,你可以写FOO(x)而不是Foo my_foo(x)。
由于主要目标是防止错误,请考虑这一点:
struct Foo { Foo( const char* ) { /* ... */ } }; enum { Foo }; int main() { struct Foo foo( "hi" ); // OK struct Foo( "hi" ); // fail Foo foo( "hi" ); // fail Foo( "hi" ); // fail }
这样,你不能忘记命名variables,你不能忘记写struct
。 详细,但安全。
声明一个参数的构造函数是明确的,没有人会无意中创build该类的对象。
例如
class Foo { public: explicit Foo(const char*); }; void fun(const Foo&);
只能用这种方式
void g() { Foo a("text"); fun(a); }
但从来没有这样(通过临时堆栈)
void g() { fun("text"); }
另见:Alexandrescu,C ++编码标准,第40项。