C ++ 17 Обновление
В C ++ 17 значение A_factory_func()
изменилось с создания временного объекта (C ++ <= 14) на простое указание инициализации любого объекта, которому это выражение инициализируется (условно говоря) в C ++ 17. Эти объекты (называемые «объектами результата») являются переменными, созданными объявлением (например, <code>a1), искусственными объектами, созданными, когда инициализация заканчивается, отбрасываются или если объект необходим для привязки ссылки (например, в A_factory_func();
) В последнем случае объект создается искусственно, что называется «временной материализацией», потому что A_factory_func()
не имеет переменной или ссылки, которая в противном случае потребовала бы существования объекта).
В качестве примеров в нашем случае, в случае специальных правил a1
и a2
говорится, что в таких объявлениях результирующий объект инициализатора prvalue того же типа, что и a1
, является переменной a1
, и следовательно, A_factory_func()
непосредственно инициализирует объект a1
. Любое промежуточное приведение функционального стиля не будет иметь никакого эффекта, потому что A_factory_func(another-prvalue)
просто «проходит» через объект результата внешнего значения prvalue, чтобы быть также объектом результата внутреннего значения prvalue.
A a1 = A_factory_func();
A a2(A_factory_func());
Зависит от того, какой тип A_factory_func()
возвращает. Я предполагаю, что он возвращает A
- тогда он делает то же самое - за исключением того, что когда конструктор копирования является явным, тогда первый потерпит неудачу. Читать 8,6 / 14
double b1 = 0.5;
double b2(0.5);
Это то же самое, потому что это встроенный тип (здесь это не тип класса). Читайте 8,6 / 14 .
A c1;
A c2 = A();
A c3(A());
Это не то же самое. Первое значение по умолчанию инициализируется, если A
не является POD, и не выполняет никакой инициализации для POD (Чтение 8.6 / 9 ). Вторая копия инициализирует: Значение - инициализирует временное значение, а затем копирует это значение в c2
(чтение 5.2.3 / 2 и 8.6 / 14 ). Это, конечно, потребует неявного конструктора копирования (Read 8.6 / 14 и 12.3.1 / 3 и 13.3.1.3 / 1 ). Третий создает объявление функции для функции c3
, которая возвращает A
и принимает указатель на функцию, возвращающую A
(Чтение 8.2 ).
Копирование в инициализации Прямая и копирование инициализации
Хотя они выглядят одинаково и должны делать то же самое, в некоторых случаях эти две формы заметно отличаются. Две формы инициализации - прямая и копируемая инициализация:
T t(x);
T t = x;
Существует поведение, которое мы можем отнести к каждому из них:
- Прямая инициализация ведет себя как вызов функции перегруженной функции: функции, в этом случае, являются конструкторами
T
(включая explicit
единиц), а аргумент равен x
. Разрешение перегрузки найдет наилучшего подходящего конструктора и при необходимости выполнит любое неявное преобразование.
- При инициализации копирования создается неявная последовательность преобразования: она пытается преобразовать
x
в объект типа T
. (Затем он может скопировать этот объект в инициализируемый объект, поэтому также необходим конструктор копирования - но это не важно ниже)
Как видите, копия инициализации в некотором роде является частью прямой инициализации в отношении возможных неявных преобразований: в то время как прямая инициализация имеет все конструкторы, доступные для вызова, и в дополнение может выполнить любое неявное преобразование, необходимое для соответствия типов аргументов, инициализация копирования может просто установить одну неявную последовательность преобразования.
Я очень старался и получил следующий код для вывода различного текста для каждой из этих форм , без использования конструкторов "очевидный" - explicit
.
#include <iostream>
struct B;
struct A {
operator B();
};
struct B {
B() { }
B(A const&) { std::cout << "<direct> "; }
};
A::operator B() { std::cout << "<copy> "; return B(); }
int main() {
A a;
B b1(a); // 1)
B b2 = a; // 2)
}
// output: <direct> <copy>
Как это работает и почему выводит этот результат?
Прямая инициализация
Сначала он ничего не знает о преобразовании. Он просто попытается вызвать конструктор. В этом случае доступен следующий конструктор с точным соответствием :
B(A const&)
Нет преобразования, тем более определяемого пользователем преобразования, необходимого для вызова этого конструктора (обратите внимание, что здесь также не происходит преобразования квалификации const). И поэтому прямая инициализация будет называть это.
Копировать инициализацию
Как сказано выше, при инициализации копирования будет создана последовательность преобразования, когда a
не имеет типа B
или не является производным от него (что, безусловно, имеет место в данном случае). Поэтому он будет искать способы сделать преобразование и найдет следующих кандидатов
B(A const&)
operator B(A&);
Обратите внимание, как я переписал функцию преобразования: тип параметра отражает тип указателя this
, который в неконстантной функции-члене является неконстантным. Теперь мы называем этих кандидатов с x
в качестве аргумента. Победителем является функция преобразования: поскольку если у нас есть две функции-кандидата, принимающие ссылку на один и тот же тип, то выигрывает версия less const (кстати, это также механизм, который предпочитает не вызов функции-члена const для неконстантных объектов).
Обратите внимание, что если мы изменим функцию преобразования на функцию-член const, то преобразование будет неоднозначным (так как тогда оба имеют тип параметра A const&
): компилятор Comeau отклоняет его должным образом, но GCC принимает его не -педантический режим. Однако переключение на -pedantic
также приводит к правильному предупреждению о неоднозначности.
Надеюсь, это поможет понять, как эти две формы различаются!