Следует ли копировать аргументы в унаследованные конструкторы при вызове базового ctor или нет? - PullRequest
4 голосов
/ 21 мая 2019

Для следующей программы:

#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 ]

3 голосов
/ 21 мая 2019

Похоже, что 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, эквивалентность mem-инициализатора в p8. Следовательно, вы получаете две копии Foo: одну в синтезированный конструктор B и одну в реальный конструктор A.

<ч />

Правила C ++ 17, в результате P0136, очень разные ( N4659: [class.inhtor.init] ): там мы напрямую вызываем конструктор A. Мы больше не добавляем новый конструктор в B - и это не механизм, который иначе выражается в языке. И поскольку мы напрямую вызываем A(Foo), это всего лишь одна копия вместо двух.

3 голосов
/ 21 мая 2019

Elision, несмотря на это, мне кажется, что Visual Studio не так:

[C++17: class.inhctor.init]/1: Когда вызывается конструктор для типа B для инициализации объектаразличного типа D (то есть, когда конструктор был унаследован ([namespace.udecl])), инициализация происходит так, как если бы по умолчанию использовался конструктор по умолчанию для инициализации объекта D и каждого подобъекта базового класса, из которогоконструктор был унаследован, за исключением того, что подобъект B инициализируется вызовом наследуемого конструктора .Полная инициализация считается одним вызовом функции;в частности, инициализация параметров унаследованного конструктора выполняется до инициализации любой части объекта D.

...