“不完整”对象实例化和输出行为

时间:2016-06-08 13:11:49

标签: c++ c++17

以下代码如何正常工作?

#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

为什么?

1 个答案:

答案 0 :(得分: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,{ {1}}是一个本地类,其类型不会转义Test,从而使实例化foo无法访问任何其他人,并决定将其删除...

...但我想这需要在链接时付出一些努力,而且我不会看到标准规定的这种行为。

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