为什么c ++使用memset(addr,0,sizeof(T))来构造一个对象?标准版还是编译错误?

alp*_*pha 5 c++ constructor perfect-forwarding c++11

这个问题与我的另一篇文章有​​关:为什么allocate_shared和make_shared这么慢

在这里,我可以更清楚地描述这个问题.

考虑以下代码:

struct A {
    char data_[0x10000];
};

class C {
public:
    C() : a_() { }
    A a_;
};

int main() {
    C c;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我发现代码中C() : a_(),编译器使用A memset(addr,0,0x10000)作为构造函数.如果类型A有一个空构造函数,则asm代码是正确的.

为了更清楚地描述问题,我写了一些测试代码:

#include <stdlib.h>

struct A {
    //A() {}
    char data_[0x10000];
    void dummy() { // avoid optimize erase by compiler
        data_[rand() % sizeof(data_)] = 1;
    }
    int dummy2() { // avoid optimize erase by compiler
        return data_[0];
    }
};

class B {
public:
    template<class ... T> B(T&...t) 
        : a_(std::forward<T>(t)...) {
    }
    A a_;
};

class C {
public:
    C() : a_() {
    }
    A a_;
};

template<class ... T>
int test(T&...t) {
    A a(t...);
    a.dummy();
    return a.dummy2();
}

int main() {
    A a;
    a.dummy();
    auto r1 = a.dummy2();

    auto r2 = test();

    B b;
    b.a_.dummy();
    auto r3 = b.a_.dummy2();

    C c;
    c.a_.dummy();
    auto r4 = c.a_.dummy2();
    return r1 + r2 + r3 + r4;
}
Run Code Online (Sandbox Code Playgroud)

我用vs2017编译代码,在windows 10,x86发布版本中.然后我检查了asm代码:

template<class ... T>
int test(T&...t) {
00E510B8  call        _chkstk (0E51CE0h)  
00E510BD  mov         eax,dword ptr [__security_cookie (0E53004h)]  
00E510C2  xor         eax,ebp  
00E510C4  mov         dword ptr [ebp-4],eax  
    A a(t...);
00E510C7  push        10000h  
00E510CC  lea         eax,[a]  
00E510D2  push        0  
00E510D4  push        eax  
00E510D5  call        _memset (0E51C3Ah)  
00E510DA  add         esp,0Ch  
    a.dummy();
00E510DD  call        dword ptr [__imp__rand (0E520B4h)]  
}
00E510E3  mov         ecx,dword ptr [ebp-4]  
Run Code Online (Sandbox Code Playgroud)

很明显函数test()调用了memset(p, 0, 0x10000).

如果我在A(行A(){})中添加一个空构造函数,编译器将删除memset.

那么为什么代码在类型A没有构造函数时调用memset但在A有构造函数时不调用memset?

它是c ++标准的一部分,还是仅仅是编译器错误?

memset(p,0,sizeof(T))显然是无用且有害的,这会减慢程序的速度.我该如何解决它?

Pas*_* By 9

A a(t...);
Run Code Online (Sandbox Code Playgroud)

会被解析为初始化at....t...为空时,如同调用它时,这将被理解为值初始化 a.

对于A没有用户提供的默认构造函数,value-initialize将其所有成员归零,因此memset.

当你提供构造函数时A,value-initialize是调用你定义为什么都不做的默认构造函数,因此不会memset调用.

这不是编译器中的错误,这是必需的行为.要删除冗余memset,您可以写A a;.在这种情况下a,默认初始化并且不会发生自动清零,无论是否有用户提供的构造函数.

†这很重要,因为A a()它将被解析为一个a带有返回类型的函数A