C++ 如何禁止临时工
对于类Foo,有没有一种方法不允许在不给它命名的情况下构造它 例如:C++ 如何禁止临时工,c++,C++,对于类Foo,有没有一种方法不允许在不给它命名的情况下构造它 例如: Foo("hi"); 只有你给它起了个名字才允许它,像下面这样 Foo my_foo("hi"); 第一个语句的生存期就是语句,第二个语句的生存期就是封闭块。在我的用例中,Foo正在测量构造函数和析构函数之间的时间。由于我从不引用局部变量,我经常忘记将其放入,并意外地更改了生存期。我想得到一个编译时错误。不,恐怕这是不可能的。但是你可以通过创建一个宏来获得同样的效果 #define FOO(x) Foo _foo(x)
Foo("hi");
只有你给它起了个名字才允许它,像下面这样
Foo my_foo("hi");
第一个语句的生存期就是语句,第二个语句的生存期就是封闭块。在我的用例中,
Foo
正在测量构造函数和析构函数之间的时间。由于我从不引用局部变量,我经常忘记将其放入,并意外地更改了生存期。我想得到一个编译时错误。不,恐怕这是不可能的。但是你可以通过创建一个宏来获得同样的效果
#define FOO(x) Foo _foo(x)
有了它,你就可以写FOO(x)而不是FOO my_FOO(x)。稍微修改一下怎么样
class Foo
{
public:
Foo (const char*) {}
};
void Foo (float);
int main ()
{
Foo ("hello"); // error
class Foo a("hi"); // OK
return 1;
}
实际上,在您的实现中,您无法做到这一点,但您可以使用此规则为您带来好处: 临时对象不能绑定到非常量引用 您可以将代码从类移动到采用非常量引用参数的独立函数。如果执行此操作,则如果临时对象尝试绑定到非常量引用,则会出现编译器错误 输出
另一个基于宏的解决方案:
#define Foo class Foo
声明Foo(“hi”)代码>扩展到类Foo(“hi”)代码>,格式不正确;但是fooa(“hi”)
扩展到类fooa(“hi”)
,这是正确的
这样做的优点是,它与现有(正确的)代码同时兼容源代码和二进制代码。(这种说法并不完全正确——请参见Johannes Schaub的评论和下面的讨论:“你怎么知道它与现有代码是源代码兼容的?他的朋友包括了他的头,并且有void f(){int Foo=0;}以前定义好类和现在的错误编译!而且,定义类FO的成员函数的每一行失败:空类Fo::Bar()({})< P>使构造器私有但给类A创建方法。 < P>几年前我为GNU C++编译器编写了一个新的警告选项。这是跟踪到的
不幸的是,GCC Bugzilla是一个墓地,在这里,经过深思熟虑的补丁(包括功能建议)将消亡
其动机是希望在使用本地对象作为小工具进行锁定和解锁、测量执行时间等的代码中准确捕获这类问题所涉及的bug。这一错误不会导致编译器错误,而是导致运行时错误。不是测量错误的时间,而是得到一个可能也可以接受的异常
要保护的任何构造函数都需要一个默认参数,在此参数上调用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”
的情况。为了防止抛出,您可以通过将保护重置为现在保护我们而不是副本的源,从而允许副本的源是临时的。现在你也明白为什么我们使用上面的指针了——它让我们变得灵活
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
和返回“hello”
的特征现在总是//OK
只是没有默认的构造函数,而是需要在每个构造函数中引用实例
#include <iostream>
using namespace std;
enum SelfRef { selfRef };
struct S
{
S( SelfRef, S const & ) {}
};
int main()
{
S a( selfRef, a );
}
#包括
使用名称空间std;
枚举SelfRef{SelfRef};
结构
{
S(SelfRef,S常数&){
};
int main()
{
S a(selfRef,a);
}
将一个参数化构造函数声明为显式,没有人会无意中创建该类的对象
比如说
class Foo
{
public:
explicit Foo(const char*);
};
void fun(const Foo&);
只能这样使用
void g() {
Foo a("text");
fun(a);
}
但决不能这样(通过堆栈上的临时文件)
参见:Alexandrescu,C++编码标准,第40项。
< P>因为主要目标是防止错误,请考虑如下:
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
}
这样,您就不会忘记命名变量,也不会忘记编写struct
。这是一个很好的例子,但是对于你来说,你可以编写自己的C++编译器,但是它严格来说是C++。在某些地方,这样的临时变量也很有用,比如从函数返回对象时(比如return std::string(“Foo”);
)不,你不能这样做,很抱歉,根据你的宗教信仰,这可能是宏可以派上用场的情况(通过usnig该类型只能通过一个总是创建变量的宏)看起来更像是我希望我的LINT工具捕捉的东西,而不是我希望通过编译器黑客从语法上阻止的东西。我本来打算向上投票,但后来我看到了“你可以创建一个宏”。好吧,修复了下划线。@Griwes-不要成为一个原教旨主义者。最好说“使用宏”比“这不可能做到”。好吧,这不可能做到。你根本没有解决这个问题,做Foo()仍然是完全合法的
。现在你在这里很固执。将Foo类重命名为一个复杂的名称,并将宏称为Foo。问题解决了。类似于:类不直接使用这个类,只通过\u Foo\u宏使用它;
很棒的Hack!注意:Foo a(“hi”)
(没有类
)这也是一个错误。我不确定我是否理解。Foo(“你好”)尝试调用void Foo(float)它会导致一个链接器错误?但是为什么要调用float版本而不是Foo-ctor?undu,嗯,你使用的是什么编译器?gcc 3.4抱怨没有到float的转换。它试图调用一个函数Foo
,因为它优先于一个类。@aleguna实际上我没有尝试运行这段代码,那只是一个错误猜猜:S,但是你回答了我的问题,我不知道函数优先于类。@ didieC.NO,<代码> fo::Foo(“Hi”)< /C> >在C++中被禁止。@ didieRC:只要它们提供了一个附加的函数,就由你不这样做。我们试图调整一种方法来达到标准中没有明确允许的东西,当然。
void g() {
Foo a("text");
fun(a);
}
void g() {
fun("text");
}
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
}