Скопируйте конструктор против конструктора std :: initializer_list - PullRequest
2 голосов
/ 10 апреля 2020

Представьте, что у нас есть:

struct S {
  struct S {
  S() { printf("%s\n", __PRETTY_FUNCTION__); }
  S(const S&) { printf("%s\n", __PRETTY_FUNCTION__); }
  S(S&&) { printf("%s\n", __PRETTY_FUNCTION__); }
  ~S() { printf("%s\n", __PRETTY_FUNCTION__); }

  S(std::initializer_list<S>) { printf("%s\n", __PRETTY_FUNCTION__); }
};

Какие конструкторы должны вызываться при S s2{S{}};? Это нормально, что g cc и clang ведут себя по-разному?

Пример: https://godbolt.org/z/qQxyp5

g cc (транк) Выход:

S::S()
S::S(std::initializer_list<S>)
S::~S()
S::~S()

выход лягушки (магистрали):

S::S()
S::~S()

1 Ответ

2 голосов
/ 10 апреля 2020

G CC здесь правильно; Инициализация списка не позволяет исключить копирование в C ++ 17.

Если вы сделали S s2(S{});, это потребуется только для вызова конструктора S по умолчанию из-за [dcl .init] /17.6.1:

Если выражение инициализатора является prvalue, а версия исходного кода cv-unqualified является тем же классом, что и класс назначения, инициализатор выражение используется для инициализации объекта назначения. [Пример: T x = T (T (T ())); вызывает конструктор T по умолчанию для инициализации x. - конец примера]

Однако это относится только к инициализации копии и прямой инициализации.

Выполнение S s2{S{}}; - это инициализация списка, которая является его собственной совершенно отдельной формой инициализации с свои правила. Поскольку S не является агрегатом, [dcl.init.list] 3.6 вступит во владение, что говорит:

В противном случае, если T является типом класса, конструкторы считается. Применимые конструкторы перечисляются, и лучший выбирается с помощью разрешения перегрузки ([over.match], [over.match.list]).

Вызов конструктора означает вызов определенной функции c с указанным c набором параметров. А это значит, что значение prvalue S{} должно использоваться для инициализации параметра в выбранном конструкторе. Это означает, что у вас есть для вызова конструктора копирования / перемещения.

Обычные, не гарантированные, исключения также не допускаются. [class.copy.elision] / 1 дает 3 обстоятельства, при которых разрешено допущение: return localVariableName, throw localVariableName и catch(TypeName), если улов соответствует броску. Этот случай, очевидно, не является ни одним из них, поэтому он не подходит для регулярного исключения.

...