2016-06-08 54 views
4

下面的代码如何正确工作?“不完整”对象实例化和输出行为

#include <cstdio> 

template<class T> 
T x = T{}; 

void foo() 
{ 
    class Test 
    { 
    public: 
     Test() { std::printf("Test::Test\n"); } 
    }; 

    Test t = x<Test>; 
} 


int main() 
{ 
    std::printf("main\n"); 
} 

输出

Test::Test 
main 

Live example

  • 为什么它打印Test::Test第一,而不是main
  • 它依赖哪个标准?仅仅是C++ 1z吗?我无法找到相关的提案。你能给我一个链接吗?
  • 什么是x在这段代码和Test t = x<Test>如何实际工作?

而且,如果我改变std::printf调用std::cout整个程序崩溃:

#include <iostream> 

template<class T> 
T x = T{}; 

void foo() 
{ 
    class Test 
    { 
    public: 
     Test() { std::cout << "Test::Test\n"; } 
    }; 

    Test t = x<Test>; 
} 


int main() 
{ 
    std::cout << "main\n"; 
} 

输出

Segmentation fault  (core dumped) ./a.out 

Live example

为什么?

+0

Google“变量模板”。 –

+0

@sleep紧pup Ok好的,谢谢。为什么它实例化没有调用'foo'函数呢? – FrozenHeart

+0

[tmp.inst/5](http://eel.is/c++draft/temp#variable%20template!definition~of):“除非显式实例化或明确指定了可变模板专门化,否则可变模板当专业化被使用时,专业化被隐含地实例化“ –

回答

3

正如其他人已经提到的,你使用了一个可变模板。

如果我没有记错,可变模板类似于这样的事情:

template<class T> 
struct X { 
    static T x; 
}; 

template<class T> 
T X<T>::x = T{}; 

那么您可以使用它,这将是这样的:

void foo() { 
    class Test { 
    public: 
     Test() { std::printf("Test::Test\n"); } 
    }; 

    Test t = X<Test>::x; 
} 

如果你试试这个,你会看到相同的结果:coliru

该模板在foo中实例化,并且发出初始化静态成员的代码。 这个初始化发生在main运行之前,所以你首先打印Test::Test

至于初始化,尽管其中的变量用于从不被调用的代码发生的事情 - 我编译器可以尝试推理foo从来没有被称为在整个程序中,Test是一个本地类,其类型不逃脱foo,因此使实例X<Test>::x无法为其他人,并决定删除它...

......但我想这将需要一些努力在链接时,我看不到这种行为强制要求标准。

此外,我不确定如果编译器/链接器甚至允许删除非局部变量的初始化,如果该初始化有副作用。