是否可以在全局范围内构造临时变量?

时间:2011-12-25 06:21:09

标签: c++ scope global

对于任意类,

template<class T>
struct S
{
    S()
    {
        cout << "hello moto\n";
    }
};

可以构造一个临时对象,该对象在创建后立即被销毁,但仅限于函数内,例如

void f()
{
    S<int>();
}

在全局范围外移动S<int>();要么不在GCC中编译,要么在VC ++中编译为函数声明。

甚至可以创建一个全局临时对象吗?


修改

虽然演示代码是模板类的形式,但它必须是我的问题;一个普通的C结构会做。但是对于一个类,很容易看出是否实际创建了这个temp,以确保该子句不被编译器解释为函数声明。

3 个答案:

答案 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设置工作通常不是一个好主意。