对于任意类,
template<class T>
struct S
{
S()
{
cout << "hello moto\n";
}
};
可以构造一个临时对象,该对象在创建后立即被销毁,但仅限于函数内,例如
void f()
{
S<int>();
}
在全局范围外移动S<int>();
要么不在GCC中编译,要么在VC ++中编译为函数声明。
甚至可以创建一个全局临时对象吗?
虽然演示代码是模板类的形式,但它必须是我的问题;一个普通的C结构会做。但是对于一个类,很容易看出是否实际创建了这个temp,以确保该子句不被编译器解释为函数声明。
答案 0 :(得分:3)
仅仅创造?肯定的是:
// vvvvvvvv creates temporary
S<int> x = S<int>();
或者为了节省全球空间:
char x = (S<int>(), 0); // uses comma operator
但是这个(ab)使用初始化。您的程序从main
开始,并且您不打算在此之前运行任意语句。你应该接受它,而不是试图解决它。
答案 1 :(得分:2)
不幸的是,纯粹的100%便携式无名自注册在C ++中是不可能的。
最常见的方法是基于未命名的命名空间:
namespace {
class MyServiceHandler : public ServiceHandler
{
...
} my_service_handler_instance;
} // unnamed namespace
上面的代码所做的是声明一个类而不在全局范围中导出其名称,然后创建一个实例(同样具有在全局范围内不可见但仅在编译单元级别可见的名称)。使用这种代码时,基类的构造函数通常会将新实例(在本例中为服务提供者)存储在某个全局目录中,以便在需要时找到它。
然而,C ++标准说静态持续时间对象的初始化可以“延迟”,但是在使用编译单元的任何对象之前肯定会执行。问题出现了:如果未创建静态持续时间实例,则无法从目录访问该服务,因此不会使用此模块的任何部分。换句话说,只有当您以独立于目录访问的其他方式访问模块时,才能保证服务注册。
这些关于延迟初始化的说法被添加到了为C ++动态加载编译单元敞开的大门,但最终结果是模块的自注册不能保证工作。还要注意,如果在程序中没有对模块的任何符号进行引用,那么链接器可能会试图优化掉整个模块(即使现实世界的编译器这样做的次数要少得多,人们希望并且如果有特殊的话。关于自定义全局内存分配器的案例。)
因此,如果您要将自注册代码放在动态加载的代码中,那么请使用专门用于初始化的平台特定代码。如果所有模块都在编译时修复,那么只需创建一个引用所有模块的模块并在主程序中引用它(例如,定义一个简单初始化所有模块的loadServices
。)
如果你需要初始化哪个模块取决于构建过程(即在OsX上有一些模块,在Windows上有其他模块),那么只需在构建中使用一个小的python或perl脚本来创建这个加载器源C ++代码。只需将模块添加到链接命令,C ++标准就无法保证。
显然初始化也是我认为更好的方法。事情在main
之前开始,整个系统处于模糊状态。例如,我从未找到静态初始化期间可以使用的内容的明确列表(启动问题)以及在静态持续时间对象的销毁期间可以使用的内容(双停止问题)。如果标准库中存在这样的列表,那么整个程序仍会存在同样的问题......(例如,你可以在这些构造函数中使用你的日志工具吗?在析构函数中?)。
在这些模糊的启动/停止阶段你做的越少越好(在我看来只是在目录中注册就好了,但是在运行时可能失败的任何东西都没有。)
在某些环境中,即使调试器在这些混乱的阶段也无法正常工作,在开始之前或main
结束之后进行调试比正常的C ++调试更加困难。我的建议是尽量避免需要它。
答案 2 :(得分:1)
C ++不允许您简单地调用全局范围内的代码。您唯一允许在全局范围内执行的操作是创建和初始化变量。因此,您将能够在全球范围内创建临时范围的唯一方式是&#34;是创建某种变量并使用其初始化来创建一个临时的。
在最技术性的意义上,以下内容创建了一个全局临时,它被复制到一个全局变量中:
Classname varName = Classname(...);
然而,几乎所有值得使用的C ++编译器都会通过完全合法的复制省略机制将其提炼为相当于Classname varName(...)
。
您可以尝试强制编译器完全按照您的意愿执行操作:
int varName = int(Classname(...));
这当然要求Classname
是一种可明确转换为int
的类型。临时将被创建,转换为整数,然后在设置整数变量后销毁临时。您并不关心该变量的价值,因此您可能只想return 0;
。
由于您正在创建和销毁对象,因此执行您正在执行的操作的唯一可能原因是构造函数/析构函数对是否更改了该对象的外部。您强烈建议尽可能避免这种情况。类构造函数/析构函数不应该伸出手来捅一些全局的东西,特别是因为全局的东西可能还没有被初始化。你所做的几乎肯定是糟糕的形式和可疑的优点。
我强烈反对。
我想写一些自我注册功能,这是在课堂上实现的。在ctor之后不使用类本身。
这是一种更好的方式来做你想做的事情:
template<typename T>
class TypeRegistrar
{
TypeRegistrar()
{
T var();
//Do registration stuff with `var`
}
};
TypeRegistrar<SomeType> reg1;
TypeRegistrar<OtherType> reg2;
sizeof(TypeRegistrar)
是sizeof(int)
。最坏情况:8个字节。我不担心。这比其他任何东西都简单,清洁等。
尽管如此,做一些预先main
设置工作通常不是一个好主意。