Рассмотрим следующий код:
#include <iostream>
class Data{
public:
Data() = default;
Data(Data const&) = delete;
Data(int) {
}
};
int main(){
int a = 0;
const std::string& rs = "abc"; // rs refers to temporary copy-initialized from char array
Data const& d_rf = a; // #2 but here can be complied
// accroding to the standard, the reference in #2 is bound to a temporary object, the temporary is copy-initialized from the expression
}
[dcl.init.ref]
Если T1 или T2 - тип класса, а T1 - не связанные со ссылкой на T2, пользовательские преобразования рассматриваются с использованием правил copy-initialization объекта типа «cv1 T1» путем пользовательского преобразования ([dcl.init], [over. match.copy], [over.match.conv]); программа некорректна, если соответствующая нереферентная инициализация копирования будет некорректной. Результат вызова функции преобразования, как описано для инициализации копирования без ссылки, затем используется для прямой инициализации ссылки. Для этой прямой инициализации пользовательские преобразования не рассматриваются
Копирование инициализации
В противном случае (т. Е. Для остальных случаев инициализации копирования) ), определенные пользователем преобразования, которые могут преобразовывать из исходного типа в целевой тип или (если используется функция преобразования) в производный класс, перечисляются, как описано в [over.match.copy], и выбирается лучший через разрешение перегрузки ([over.match]). Если преобразование не может быть выполнено или является неоднозначным, инициализация неверна. Выбранная функция вызывается с выражением инициализатора в качестве аргумента; если функция - конструктор, то вызов - это prvalue cv-неквалифицированной версии целевого типа, чей результирующий объект инициализируется конструктором. Вызов используется для прямой инициализации, согласно приведенным выше правилам, объекта, являющегося местом назначения инициализации копирования.
В соответствии со стандартом тип a
равен int
, а тип инициализированной ссылки - Data
, поэтому от int
до Data
, пользовательские преобразования рассматриваются с использованием правил copy-initialization объекта типа «cv1 T1» путем пользовательского преобразования . Это означает, что Data const& d_rf = a;
можно перевести на Data temporary = a; Data const& d_rf = temporary;
. Для Data temporary = a;
, хотя copy elision существует, конструктор копирования / перемещения должен быть проверен, доступен ли он , но конструктор копирования из class Data
был удален, почему его можно выполнить?
Вот некоторые цитаты из стандарта
Копировать инициализацию ссылки из enseignement
Копировать инициализацию ссылки из cppreference
Если ссылка является ссылкой lvalue:
Если объект является выражением lvalue, а его тип равен T или является производным от T, и равен ему или меньше с квалификацией cv, тогда ссылка привязывается к объекту, идентифицированному lvalue, или его подобъекту базового класса.
Если объект является выражением lvalue и его тип неявно преобразуется в тип, который является либо T, либо производным от T с равной или меньшей cv-квалификацией учитываются неявные функции преобразования типа источника и его базовых классов, которые возвращают ссылки на lvalue, и лучшая из них выбрано разрешением перегрузки. Затем ссылка привязывается к объекту, идентифицированному lvalue, возвращаемым функцией преобразования (или его подобъекту базового класса)
В противном случае, если ссылка является либо ссылкой rvalue, либо ссылкой lvalue на const:
Если объект является xvalue, классом prvalue, массивом prvalue или типом функции lvalue, который является либо T, либо производным от T, с равной или меньшей квалификацией cv, то ссылка привязывается к значению выражения инициализатора или к его базовому подобъекту.
Если объект является выражением типа класса, которое может быть неявно преобразовано в xvalue, prvalue класса или значение функции типа, которое является или T или производным от T, равным или меньшим cv-квалифицированным, затем ссылка привязывается к результату преобразования или к его базовому подобъекту.
В противном случае создается временный тип T и инициализируется из объекта с помощью копирования. Ссылка затем привязана к этому временному. Применяются правила инициализации копирования (явные конструкторы не рассматриваются).
[пример:
const std :: string & rs = "ab c"; // rs ссылается на временное инициализированное копированием из массива символов]
ОБНОВЛЕНИЕ:
Мы рассматриваем код в N337
в соответствии со стандартом, тип a
типа int
, а тип назначения, на который ссылается ссылка, Data
, поэтому компилятору необходимо создать временный типа Data
по копировать инициализацию . Здесь нет никаких сомнений, поэтому мы сосредоточимся на copy initialization . Тип источника - int
, а тип назначения - Data
, эта ситуация соответствует:
В противном случае (т. Е. Для остальных случаев инициализации копирования) пользовательские последовательности преобразования, которые могут преобразование из типа источника в тип назначения или (если используется функция преобразования) в его производный класс перечисляются, как описано в 13.3.1.4, и лучший выбирается с помощью разрешения перегрузки (13.3). Если преобразование не может быть выполнено или является неоднозначным, инициализация неверна. Выбранная функция вызывается с выражением инициализатора в качестве аргумента; если функция является конструктором, вызов инициализирует временную версию cv-unqualified версии назначения. Временное является prvalue. Результат вызова (который является временным для случая конструктора) затем используется для прямой инициализации, согласно вышеприведенным правилам, объекта, являющегося местом назначения инициализации копирования . В некоторых случаях реализация позволяет исключить копирование, присущее этой прямой инициализации, путем создания промежуточного результата непосредственно в инициализируемом объекте;
ПРИМЕЧАНИЕ Жирная часть, это не означает, что значение int
напрямую инициализирует временное значение Data::Data(int)
. Это означает, что int
сначала преобразуется в Data
с помощью Data::Data(int)
, затем этот результат напрямую инициализирует временный объект, который является объектом, который является здесь пунктом инициализации копирования . Если мы используем код для express жирной части, это похоже на Data temporary(Data(a))
.
Вышеуказанные правила здесь:
- Если инициализация является прямой инициализацией, или если это инициализация копирования, когда cv-неквалифицированная версия исходного типа является тем же классом, или производным классом класса назначения, рассматриваются конструкторы. Применимые конструкторы перечислены (13.3.1.3), и лучший выбирается через разрешение перегрузки (13.3). Выбранный таким образом конструктор вызывается для инициализации объекта с выражением инициализатора или списком выражений в качестве аргументов. Если конструктор не применяется, или разрешение перегрузки неоднозначно, инициализация некорректна.
Пожалуйста, вернитесь к Data temporary(Data(a))
. Очевидно, что конструктор копирования / перемещения лучше всего подходит для аргумента Data (a). Однако Data(Data const&) = delete;
, поэтому конструктор копирования / перемещения недоступен. Почему компилятор не сообщает об ошибке?