如何禁止临时工
How to disallow temporaries
对于类foo,是否有一种方法可以禁止构造它而不给它命名?
例如:
Foo("hi");
,只有在您给它的名字时才允许它,如以下内容吗?
Foo my_foo("hi");
第一个寿命只是语句,第二个是封闭块。在我的用例中,Foo
正在测量构造函数和驱动器之间的时间。由于我从不参考局部变量,因此我经常忘记将其放入,而意外地改变了一生。我想获得编译时间错误。
另一个基于宏的解决方案:
#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(){} {}" ))
一个小hack
怎么样class Foo
{
public:
Foo (const char*) {}
};
void Foo (float);
int main ()
{
Foo ("hello"); // error
class Foo a("hi"); // OK
return 1;
}
将构造函数私有化,但给出了类A create 方法。
这不会导致编译器错误,而是运行时错误。您没有测量错误的时间,而是会得到一个例外,这也可能是可以接受的。
您要守卫的任何构造函数都需要默认参数,以便在哪个 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项目中跟踪的。
不幸的是,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;
}
输出
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 );
}
不,恐怕这是不可能的。但是您可以通过创建宏来获得相同的效果。
#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
}
这样,您就不会忘记命名该变量,也不会忘记编写struct
。冗长但安全。
将单参数构造函数称为明确的,没有人会无意间创建该类别的对象。
例如
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。
- ISO C++禁止声明没有类型的"setInputNombre"
- 禁止显示由于常量为零而比较始终为假的警告
- 禁止在控制台上记录谷神星
- 禁止显示有关包含文件中 #pragma 包的警告
- 禁止指针和整数之间的比较C++
- ISO C++禁止指针和整数 [-fpermissive] [c++] 之间的比较
- 为什么我会收到此警告:ISO c++ 禁止可变长度数组"v"[-Wvla]
- 当空基类也是成员变量时,为什么禁止空基优化?
- ISO 中禁止可变长度数组 C++崇高文本中的错误 3.
- 标记类以禁止操作
- 禁止子函数调用父级的抽象(或虚拟)函数
- Linux C 只禁止我的程序使用核心转储
- 错误:ISO C++禁止可变长度数组"subVec"[-Werror=vla]
- 如何禁止在 g++ 中使用但从未定义警告的内联函数
- 捕获/禁止发送到 std::cout 的 OpenCV 警告
- Qt 错误 iso c++ 禁止指针和整数之间的比较 -permissive
- 为什么禁止建造 istreams?
- 是否可以禁止在for循环体内部修改循环变量
- 在c++中,如果首先禁止默认构造,那么禁止复制构造有意义吗
- 如何禁止临时工