Следует ли копировать аргументы в унаследованные конструкторы при вызове базового ctor или нет?

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

#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 ответа

Решение

Похоже, что 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)это только одна копия вместо двух.

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

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

Другие вопросы по тегам