2016-08-06 11 views
9

Там есть пункт о гарантированной копии элизии в C++ проект n4606 [dcl.init] 17.6:Как гарантированное копирование elision работает в инициализации списка в C++ 1z?

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

Существует также Q&A говорит о том, как это работает.

Для меня я понял, что правило, которое я цитировал, гарантирует, что никакие ctors не должны взаимодействовать, когда выражение инициализатора является prvalue, а cv-неквалифицированная версия типа источника - это тот же класс, что и класс назначения. Так что нет нужды, чтобы проверить наличие копии или перемещения CTOR, что делает следующие коды, чтобы быть законным в C++ 17:

struct A { 
    A() {} 
    A(A const &) = delete; 
    A(A &&) = delete; 
}; 
A f() { return A(); } // it's illegal in C++14, and suppose to be legal in C++17 

Однако то, что сводит меня с ума, я не могу найти аналогичные правила в списке раздел инициализации в проекте C++ n4606. То, что я нашел ([dcl.init.list] 3,6)

[...]

  • В противном случае, если T является тип класса, конструкторы рассматриваются. Применяемые конструкторы перечисляются, а лучший выбирается с помощью разрешения перегрузки (13.3, 13.3.1.7). Если для преобразования любого из аргументов требуется сужение преобразования (см. Ниже), программа плохо сформирована. [...]

Поскольку список инициализация имеет более высокий приоритет, чем первое правило я цитировал, мы должны учитывать правило в списке инициализируются секции при инициализации является инициализатор-лист. Как мы видим, конструкторы учитываются при инициализации списка типа класса T. Таким образом, продолжение предыдущего примера будет

A ff() { return {A()}; } 

юридический в C++ 17? И может ли кто-нибудь найти, где в стандартном проекте указывается, как работает гарантированное копирование при инициализации списка?

+2

Гарантированное копирование будет работать со сценарием инициализации списка только для агрегатов из-за специального правила для агрегатов, инициализированных с объектом того же типа в C++ 17 .. Но остальные случаи, похоже, не гарантируются гарантированным копировать. Но помните, что вы можете просто отбросить фигурные скобки, и это сработает. Я не вижу причин поместить фигурные скобки. –

+0

Обратите внимание, что даже не гарантированное копирование будет работать здесь (afaics .. это не инициализация, а конкретное приложение разрешения перегрузки и вызов конструктора .. поэтому он не должен быть покрыт правилами элиции, если явно), поэтому это не проблема, введенная C++ 17. –

+0

Я поставил вопрос на обсуждение std: https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/3EvAt9v4dYE –

ответ

5

Guaranteed elision работает путем переопределения выражений выражения, означающих «инициализирует объект». Они больше не создают временные рамки; Временные структуры вместо этого построены определенными , использует выражений prvalue.

Обратите внимание на частое использование слова «выражение» выше. Я указываю на это из-за одного очень важного факта: список с фиксированным номером не является выражением. Стандарт очень понятен.Это не выражение, и только выражения могут быть prvalues.

Действительно, рассмотрим раздел стандарта на элизии:

Это элизия копирования/перемещения операций, называется копия элизия, допускается в следующих случаях:

  • в ответном заявлении в функции с возвратным типом класса, когда выражение - это название нелетучего автоматического объекта ...
  • ...
  • , когда объект временного класса, который не был связан с ссылкой (12.2) будет скопирован/перемещен в объект класса с тем же сортом-неквалифицированным типом

Они все связанно с выражениями (объекты временного класса являются выражения). Исчерпывающие-init-списки не являются выражениями.

Как таковой, если вы выдаете return {anything};, то построение возвращаемого значения от anything не будет отменено, независимо от того, что anything есть. Согласно стандарту, конечно; компиляторы могут отличаться из-за ошибок.

Теперь, если у вас есть выражение prvalue того же типа, что и возвращаемое значение, вы вряд ли захотите ввести return {prvalue}; вместо return prvalue;. И если выражение было другого типа, тогда оно не может претендовать на элицию в любом случае.

+0

Ну, я просто не видел правильного использования гарантированной копии elision in list-initialization, написав что-то вроде 'return {prvalue};', но я не уверен в этом. Кажется, что нет необходимости гарантировать копирование в инициализации списка, и поэтому стандартный проект не указывает его? – Carousel

+1

@ Карусель: Я не уверен, насколько я могу это сказать. Elision основан на * выражениях *. Исчерпывающие-init-списки не являются выражениями. Следовательно, к ним не относится. –

+0

@NicolBolas, они не являются выражениями, так же как '(foo)' in 'T t (foo)' не является выражением и '= foo' в' T t = foo'. Однако 'foo' является одним, так же как' prvalue' является одним из '{prvalue}'. Таким образом, говорящие «начальные» списки инициализаций не являются выражениями, поэтому в инициализации списка отсутствует исключение для копирования », кажется мне не кажется. Фактически, некоторые его формы позволяют копировать эликсирование, как я показал в своем комментарии выше. –