对于Foo类,有没有办法在不给它命名的情况下禁止构造它?
例如:
Foo("hi");
Run Code Online (Sandbox Code Playgroud)
如果你给它一个名字,只允许它,如下所示?
Foo my_foo("hi");
Run Code Online (Sandbox Code Playgroud)
第一个的生命周期只是语句,第二个是封闭块.在我的用例中,Foo
是测量构造函数和析构函数之间的时间.因为我从不参考局部变量,所以我常常忘记将其放入,并且意外地改变了生命周期.我想改为编译错误.
eca*_*mur 101
另一个基于宏的解决方案:
#define Foo class Foo
Run Code Online (Sandbox Code Playgroud)
声明Foo("hi");
扩大到class Foo("hi");
,形成不良; 但Foo a("hi")
扩展到class Foo a("hi")
,这是正确的.
这样做的好处在于它既是源代码与二进制兼容现有的(正确)的代码.(这个说法并不完全正确 - 请参阅Johannes Schaub的评论以及随后的讨论:"你怎么知道它与现有代码的源代码兼容?他的朋友包括他的标题并且有void f(){int Foo = 0;}而此前编译罚款,现在miscompiles此外,每一行定义类Foo的成员函数失败:无效类Foo ::巴(){}")
小智 71
怎么样一个小黑客
class Foo
{
public:
Foo (const char*) {}
};
void Foo (float);
int main ()
{
Foo ("hello"); // error
class Foo a("hi"); // OK
return 1;
}
Run Code Online (Sandbox Code Playgroud)
dch*_*tri 45
使构造函数为私有,但为类提供create方法.
Joh*_*itb 25
这个不会导致编译器错误,但会导致运行时错误.而不是测量错误的时间,你得到一个也可以接受的例外.
您想要保护的任何构造函数都需要一个调用它的默认参数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;
};
Run Code Online (Sandbox Code Playgroud)
特点是:
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();
}
Run Code Online (Sandbox Code Playgroud)
的情况下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;
};
Run Code Online (Sandbox Code Playgroud)
为特色f2
,f3
和return "hello"
现在始终// OK
.
Kaz*_*Kaz 18
几年前,我为GNU C++编译器编写了一个补丁,为这种情况添加了一个新的警告选项.这是在Bugzilla项目中跟踪的.
不幸的是,GCC Bugzilla是一个埋葬地,其中考虑周全的补丁包含功能建议将会消失.:)
这是因为希望在代码中捕获这个问题主题的错误,这些错误使用本地对象作为锁定和解锁的小工具,测量执行时间等等.
按照您的实现,您无法执行此操作,但您可以使用此规则:
临时对象不能绑定到非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;
}
Run Code Online (Sandbox Code Playgroud)
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&)’
Run Code Online (Sandbox Code Playgroud)
只是没有默认的构造函数,并且需要在每个构造函数中引用一个实例.
#include <iostream>
using namespace std;
enum SelfRef { selfRef };
struct S
{
S( SelfRef, S const & ) {}
};
int main()
{
S a( selfRef, a );
}
Run Code Online (Sandbox Code Playgroud)
不,我担心这是不可能的.但是你可以通过创建一个宏来获得相同的效果.
#define FOO(x) Foo _foo(x)
Run Code Online (Sandbox Code Playgroud)
有了这个,你可以写FOO(x)而不是Foo my_foo(x).