繁体   English   中英

当 {} 与 new 一起使用来创建 std::array 时,会绕过 T 的默认构造函数<T, n>

[英]Default constructor of T is bypassed when {} are used with new to create std::array<T, n>

让我们考虑这个类:

class A {
public:
    A() = delete;

    A(int i) :
            i_m(i) {
        std::cout << __PRETTY_FUNCTION__ << ' ' << i_m << '\n';
    }

    ~A() {
        std::cout << __PRETTY_FUNCTION__ << ' ' << i_m << '\n';
    }

private:
    int i_m{1234567890};
};

默认构造函数被显式删除,因此 AFAIK A只能从整数构造。 永远不会使用数据成员i_m的默认初始化。

让我们考虑这个程序:

int main() {
    using T = std::array<A, 2>;

    //T a;
    // error: use of deleted function 'std::array<A, 2>::array()'
    // note: 'std::array<A, 2>::array()' is implicitly deleted because the default definition would be ill-formed
    // error: use of deleted function 'A::A()'

    //T b{};
    // error: use of deleted function 'A::A()'
}

同样,这对我来说似乎完全没问题。

现在让我们考虑另一个程序:

int main() {
    using T = std::array<A, 2>;
    auto foo = new T{};
    delete foo;

    auto foo_init = new T{1, 2};
    delete foo_init;

//  auto zorg = new T();
//  delete zorg;
//  error: use of deleted function 'std::array<A, 2>::array()'
//  note: 'std::array<A, 2>::array()' is implicitly deleted because the default definition would be ill-formed:
//  error: use of deleted function 'A::A()'

    auto zorg_init = new T({3, 4});
    delete zorg_init;
}

此代码确实编译(没有警告)并生成以下输出:

A::~A() 0
A::~A() 38870160
A::A(int) 1
A::A(int) 2
A::~A() 2
A::~A() 1
A::A(int) 3
A::A(int) 4
A::~A() 4
A::~A() 3

而现在的东西似乎罚款给我。 这行auto foo = new T{};怎么可能auto foo = new T{}; 不被视为格式错误? 此处完全绕过了类A的初始化。

这段代码不编译毫无价值:

int main() {
    auto p = new A{};
    delete p;
}

错误是预期的:

error: use of deleted function 'A::A()'

我使用以下选项测试了这些代码: -Wall -Wextra -pedantic -std=c++17 gcc -v在我的电脑上给出: gcc version 7.2.0 (x86_64-posix-sjlj-rev0, Built by MinGW-W64 project)

任何解释将不胜感激:)


PS:在编译器资源管理器中进行详尽的测试后(使用示例https://godbolt.org/z/5VZLU_ ),这似乎是 gcc 7.x 中的一个问题。 实际上,只有 7.x 版本的 gcc 才能编译此示例。 gcc 6.4 没有。 gcc 8.1 都没有。 叮当。 msvc 都没有。

你能确认这里没有 UB,这真的是一个编译器错误吗?

T b{}; 不编译而new T{}很明显这是一个编译器错误。 然而,标准本身可能有缺陷。 这是为什么。


这个问题的技术答案是:代码格式良好,因为标准明确说明了这一点。 [阵列概览]/2

array是一个聚合,可以使用最多N元素进行列表初始化,这些元素的类型可以转换为T

初始化器{}确实包含“最多N元素,其类型可以转换为T ”——它包含 0 个元素,所有元素可以转换为A


那么元素是如何初始化的呢? 这个标准不是很清楚。 这是[array.overview]/3所说的:

array满足容器和可逆容器([container.requirements])的所有要求,除了默认构造的array对象不为空并且swap没有恒定的复杂性。 [...]

(强调我的)

标准没有提到这个非空array的元素是如何初始化的。


假设std::array是这样实现的(对于N == 2 ):

template <class T, std::size_t N>
struct array {
    T __elem[N];
};

然后根据[dcl.init.aggr]/8{}复制初始化元素:

如果列表中的初始化子句少于非联合聚合中的元素,则每个未显式初始化的元素将按如下方式初始化:

  • ( 8.1 ) 如果元素有一个默认的成员初始值设定项([class.mem]),则元素从该初始值设定项进行初始化。

  • ( 8.2 ) 否则,如果元素不是引用,则从空的初始化列表 ([dcl.init.list]) 中复制初始化该元素。

  • ( 8.3 ) 否则,程序格式错误。

[...]

根据[dcl.init.list]/3 格式错误(选择了删除的默认构造函数)。

这意味着标准认为通用实现是错误的,这可能不是故意的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM