Рассмотрим следующий код:
#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
}
Если T1 или T2 относятся к типу класса, а T1 не связан со ссылкой на T2, пользовательские преобразования рассматриваются с использованием правил для инициализации копирования объекта типа «cv1 T1» пользователем. определенное преобразование ([dcl.init], [over.match.copy], [over.match.conv]); программа будет сформирована неправильно, если соответствующая не ссылочная инициализация копии будет неправильно сформирована. Результат вызова функции преобразования, как описано для инициализации копии без ссылки, затем используется для прямой инициализации ссылки. Для этой прямой инициализации определенные пользователем преобразования не рассматриваются.
В противном случае (т. Е. Для остальных случаев инициализации копирования) определяемые пользователем преобразования, которые могут преобразовывать исходный тип в целевой тип или (когда используется функция преобразования) в его производный класс, перечисляются, как описано в [over. match.copy], а лучший выбирается путем разрешения перегрузки ([over.match]). Если преобразование не может быть выполнено или неоднозначно, инициализация сформирована неправильно. Выбранная функция вызывается с выражением инициализатора в качестве аргумента; если функция является конструктором, вызов является prvalue неквалифицированной cv версии целевого типа, чей объект результата инициализируется конструктором. Вызов используется для прямой инициализации в соответствии с приведенными выше правилами объекта, который является адресатом инициализации копии.
Согласно стандарту тип a
- int
, а тип инициализированной ссылки - Data
, поэтому от int
до Data
, определяемые пользователем преобразования рассматриваются с использованием правил для инициализации копирования em > объекта типа «cv1 T1» посредством пользовательского преобразования. Это означает, что Data const& d_rf = a;
можно перевести на Data temporary = a; Data const& d_rf = temporary;
. Для Data temporary = a;
, несмотря на то, что исключение копирования существует, конструктор копирования / перемещения необходимо проверить, доступен ли он, но конструктор копирования для 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 = "abc"; // rs относится к временному инициализированному копией из массива символов]
ОБНОВИТЬ:
Мы рассматриваем код под N337.
согласно стандарту тип значения a
- int
, а тип назначения, на который ссылается ссылка, - Data
, поэтому компилятору необходимо сгенерировать временный объект типа Data
с помощью инициализации копии. Здесь нет никаких сомнений , поэтому мы сосредотачиваемся на инициализации копии. Тип источника - int
, а тип назначения - Data
, эта ситуация соответствует:
В противном случае (т. Е. Для остальных случаев копирования-инициализации) определяемые пользователем последовательности преобразования, которые могут преобразовывать из исходного типа в целевой тип или (когда используется функция преобразования) в его производный класс, перечисляются, как описано в 13.3. 1.4, а лучший выбирается по разрешению перегрузки (13.3). Если преобразование не может быть выполнено или неоднозначно, инициализация сформирована неправильно. Выбранная функция вызывается с выражением инициализатора в качестве аргумента; если функция является конструктором, вызов инициализирует временную версию cv-неквалифицированной версии целевого типа. Временное - это prvalue. Результат вызова (который является временным для случая конструктора) затем используется для прямой инициализации, в соответствии с приведенными выше правилами, объекта, который является адресатом инициализации копирования. В некоторых случаях реализации разрешается исключить копирование, присущее этой прямой инициализации, путем построения промежуточного результата непосредственно в инициализируемом объекте;
ОБРАТИТЕ ВНИМАНИЕ, что выделенная жирным шрифтом часть не означает, что значение int
напрямую инициализирует временное значение Data::Data(int)
. Это означает, что int
сначала преобразуется в Data
с помощью Data::Data(int)
, затем этот результат напрямую инициализирует временный объект, который здесь является объектом, являющимся адресатом инициализации копии. Если мы используем код для обозначения жирной части, это похоже на Data temporary(Data(a))
.
Вышеупомянутые правила здесь:
- Если инициализация является прямой инициализацией или копией-инициализацией, когда cv-неквалифицированная версия исходного типа является тем же классом, что и класс назначения, или производным классом от него, рассматриваются конструкторы. Применимые конструкторы перечислены (13.3.1.3), и лучший из них выбирается посредством разрешения перегрузки (13.3). Выбранный таким образом конструктор вызывается для инициализации объекта с выражением инициализатора или списком выражений в качестве аргумента (ов). Если конструктор не применяется или разрешение перегрузки неоднозначно, инициализация сформирована неправильно.
Вернитесь к Data temporary(Data(a))
. Очевидно, что конструктор копирования / перемещения лучше всего подходит для аргумента Data (a). Однако Data(Data const&) = delete;
, поэтому конструктор копирования / перемещения недоступен. Почему поставщик не сообщает об ошибке?
#1
действителен с C ++ 17 - person Jarod42   schedule 20.01.2020#1
недействителен, но#2
действителен, почему? - person xmh0511   schedule 20.01.2020#1
используется для проверки инициализированной копии, для которой нужен конструктор копирования, и рассмотрения#2
, ссылка привязана к временному объекту, который инициализируется копией изa
, но#2
выполняется без ошибок - person xmh0511   schedule 20.01.2020