Ответ 1
Первоначальный проект для добавления формулировки для вычитания копии был P0620R0, в котором упоминается
Настоящий документ предназначен для решения
- Направление на обертку и копирование с EWG в понедельник в Kona
Некоторые замечания по этой встрече доступны на https://botondballo.wordpress.com/2017/03/27/trip-report-c-standards-meeting-in-kona-february-2017/:
Копирование против поведения обертывания. Предположим, что
a
является переменной типаtuple<int, int>
, и мы пишемtuple b{a};
, Если типb
являетсяtuple<int, int>
(поведение "копирование") илиtuple<tuple<int, int>>
(поведение "wrapping")? Этот вопрос возникает для любого типа, подобного оболочке (например,pair
,tuple
илиoptional
), который имеет как конструктор копирования, так и конструктор, который берет объект обернутого типа. EWG считает, что копирование было лучшим дефолтом. Были некоторые разговоры о том, что поведение зависит от синтаксиса инициализации (например, синтаксис{ }
всегда должен быть обернут), но EWG чувствовал, что введение новых несоответствий между поведением различных синтаксисов инициализации принесет больше вреда, чем пользы.
@kiloalphaindia объяснил это в комментарии:
Если # 2 будет использовать
A::A(T)
мы закончим сy
beeingA<A<int>>
<A <intA<A<int>>
. [...]
Это правильно. Конструктор A<A<int>>::A(A<int>)
имеет точное совпадение в типе параметра. С другой стороны, вы также правы, что A<int>::A(const A<int> &)
в этом случае были бы предпочтительными.
Но рассмотрим эту альтернативу, где эквивалент функции показывает, что A<A<int>>
было бы предпочтительным, если бы не кандидат на копирование:
template <typename T>
struct A {
A(T &&);
A(const A<T> &);
};
template <typename T> auto f(T &&) -> A<T>;
template <typename T> auto f(const A<T> &) -> A<T>;
int main() {
A x1(42); // A<int>
A y1 = std::move(x1); // A<int>
auto x2 = f(42); // A<int>
auto y2 = f(std::move(x2)); // A<A<int>>
}