是否在调用基本ctor时将args复制到继承的构造函数?

时间:2019-05-21 14:34:43

标签: c++ visual-studio c++17 inherited-constructors

对于以下程序:

#include <iostream>

struct Foo
{
    Foo() { std::cout << "Foo()\n"; }
    Foo(const Foo&) { std::cout << "Foo(const Foo&)\n"; }
    ~Foo() { std::cout << "~Foo()\n"; }
};

struct A
{
    A(Foo) {}
};

struct B : A
{
    using A::A;
};

int main()
{
    Foo f;
    B b(f);
}

GCC给出:

$ g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
Foo()
Foo(const Foo&)
~Foo()
~Foo()

VS 2017(也在C ++ 17模式下)提供:

Foo()
Foo(const Foo&)
Foo(const Foo&)
~Foo()
~Foo()
~Foo()

谁是对的,为什么?

(让我们也不要忘记VS 2017没有正确执行强制的复制省略。因此,可能是复制是“真实的”,但GCC根据C ++ 17规则将其删除,而VS没有...)

2 个答案:

答案 0 :(得分:3)

尽管有省略,但在我看来Visual Studio是错误的:

  

[C++17: class.inhctor.init]/1:调用类型为B的构造函数以初始化不同类型D的对象时(即,构造函数被继承([namespace.udecl])时) ,初始化的过程就像使用默认的默认构造函数来初始化D对象和继承该构造函数的每个基类子对象一样,不同之处在于B子对象是通过调用来初始化的继承的构造函数。完整的初始化被认为是单个函数调用。特别是,在初始化D对象的任何部分之前,先对继承的构造函数的参数进行初始化。

答案 1 :(得分:3)

看来Visual Studio尚未实现P0136。正确的C ++ 17行为是一个副本,正确的C ++ 14行为是两个副本。


C ++ 14规则(N4140:[class.inhctor])会解释:

struct B : A
{
    using A::A;
};

为:

struct B : A
{
    B(Foo f) : A(f) { }
};

在p3中指定了引入的构造函数,在p8中指定了mem-initializer等效项。因此,您将获得Foo的两个副本:一个副本到B的综合构造函数中,一个副本到A的真实构造函数中。


由于P0136而导致的C ++ 17规则非常不同(N4659:[class.inhtor.init]):在那里,我们直接调用A的构造函数。就像我们不再向B中添加新的构造函数一样,它也不是该语言可以表达的一种机制。而且由于我们直接调用A(Foo),所以这只是一个副本,而不是两个副本。