При выполнении копирования-исключения компилятор не рассматривает конструктор копирования в разрешении перегрузки, когда конструктор перемещения удаляется. Почему?

Я могу понять, что компилятор выполняет copy-elision в приведенном ниже коде, поскольку конструкторы копирования и перемещения не вызываются в так называемом copy-initialization, выполненном в main(). См. живой пример.

#include <iostream>
struct S {
    S() = default;
    S(const S&) { std::cout << "copy ctor" << '\n'; }
    S(S&&) { std::cout << "move ctor" << '\n'; }
};

int main() {
    S s = S(); 
}

Но я не могу понять, почему код не компилируется, когда я удаляю конструктор перемещения, как показано ниже:

#include <iostream>
struct S {
    S() = default;
    S(const S&) { std::cout << "copy ctor" << '\n'; }
    S(S&&) = delete;
};

int main() {
    S s = S(); 
}

Я не могу найти ничего в §12.8/32 (N4140), что могло бы запретить использование или исключение конструктора копирования в данном случае. Это предложение привлекло мое внимание в §12.8/32, которое, по-видимому, указывает на то, что конструктор копирования должен был учитываться при разрешении перегрузки:

Если первое разрешение перегрузки завершилось неудачно или не было выполнено, или если тип первого параметра выбранного конструктора не является ссылкой rvalue на тип объекта (возможно, cv-квалифицированным), разрешение перегрузки выполняется снова, рассматривая объект как объект. значение.

Изменить

Из одного из комментариев TC ниже я понимаю, что когда копируемый объект обозначается значением r, компилятор , согласно §12.8/32, не рассматривает конструктор-копию как кандидата на копию, даже если копия в любом случае будет исключена. То есть конечным результатом будет создание объекта s с конструктором по умолчанию. Вместо этого в этой ситуации Стандарт предписывает (где??) код быть неправильно сформированным. Если мое понимание этой схемы совершенно неверно, это не имеет для меня никакого смысла.


person François-Marie Arouet    schedule 02.08.2015    source источник
comment
Разрешение перегрузки не завершается ошибкой при обнаружении удаленной функции. Удаленные функции не являются несуществующими, они в некотором смысле не удалены. Их просто недопустимо вызывать (их можно вызывать, и если вы это сделаете, это будет ошибкой).   -  person dyp    schedule 02.08.2015
comment
И в любом случае эта цитата из 12.8/32 не применима.   -  person T.C.    schedule 02.08.2015
comment
Вне операторов return этот абзац применим только тогда, когда, среди прочего, копируемый объект обозначен lvalue. S() не является lvalue.   -  person T.C.    schedule 02.08.2015
comment
@Т.С. Итак, вы говорите, что когда копируемый объект является значением r, разрешение перегрузки не будет выполняться с использованием двухэтапного процесса. Это правильно? Если это так, как я могу принудительно использовать конструктор копирования в этом примере? Есть ли работа вокруг?   -  person François-Marie Arouet    schedule 02.08.2015
comment
Не удалять явно конструктор перемещения? Как правило, нет веской причины, когда вы все еще разрешаете копирование.   -  person T.C.    schedule 02.08.2015
comment
Но как мне избежать исключения копии?   -  person François-Marie Arouet    schedule 02.08.2015
comment
Почему вы хотите избежать исключения копирования?   -  person T.C.    schedule 02.08.2015
comment
Я просто хочу понять стандарт   -  person François-Marie Arouet    schedule 02.08.2015
comment
@Т.С. См. мое редактирование выше.   -  person François-Marie Arouet    schedule 03.08.2015
comment
Используйте переключатель компилятора, чтобы отключить копирование, вы увидите, что ваш код по-прежнему незаконен.   -  person M.M    schedule 03.08.2015
comment
@François-MarieArouet Я понимаю, что когда копируемый объект обозначается значением r, компилятор, в соответствии с §12.8/32, не рассматривает конструктор копирования как кандидата для копии, даже если копия в любом случае будет исключена , неправильно, в вашем случае разрешение перегрузки сначала находит конструктор перемещения (потому что это лучшее совпадение), поэтому само разрешение перегрузки завершается успешно, поскольку этот конструктор перемещения удаляется, компилятор сообщает об ошибке. если вы не предоставили конструктор перемещения, то конструктор копирования будет выбран разрешением перегрузки   -  person Piotr Skotnicki    schedule 03.08.2015
comment
@François-MarieArouet удаленный конструктор обрабатывается так, как если бы он был объявлен пользователем, и он действительно участвует в разрешении перегрузки.   -  person Piotr Skotnicki    schedule 03.08.2015
comment
@PiotrSkotnicki Но почему удаленные функции должны участвовать в разрешении перегрузки?   -  person François-Marie Arouet    schedule 03.08.2015
comment
@François-MarieArouet, потому что =delete не означает, что этой функции не существует. Если вы не хотите, чтобы функция существовала, просто не объявляйте ее. То есть =delete означает, что при попытке использовать такую ​​удаленную функцию компилятор должен выдать ошибку.   -  person Piotr Skotnicki    schedule 03.08.2015
comment
@PiotrSkotnicki Спасибо за ваши полезные и проницательные комментарии.   -  person François-Marie Arouet    schedule 04.08.2015
comment
@PiotrSkotnicki В чем разница в этом случае между частным и удаленным конструктором перемещения?   -  person François-Marie Arouet    schedule 04.08.2015
comment
@François-MarieArouet, сам класс и его друзья смогут использовать конструктор частного перемещения.   -  person Piotr Skotnicki    schedule 04.08.2015
comment
@PiotrSkotnicki Отлично. Я просто забыл об этой возможности.   -  person François-Marie Arouet    schedule 05.08.2015


Ответы (1)


Это не имеет ничего общего с копированием или конструкторами; это просто разрешение перегрузки.

Если у нас есть пара перегрузок:

void f( T&& rv );
void f( const T& lv );

тогда правила разрешения перегрузки говорят, что f( T{} ) лучше соответствует f(T&&).

Удаление копии может исключать копию или перемещаться, но только тогда, когда код четко определен (даже если компилятор решает не реализовывать исключение копии). Ваш код не является четко определенным, потому что он указывает на вызов удаленной функции.

person M.M    schedule 03.08.2015
comment
Но почему удаленная функция должна участвовать в разрешении перегрузки? - person François-Marie Arouet; 03.08.2015
comment
@ Франсуа-Мари Аруэ, так оно и есть. В этом случае он кажется более гибким: вы можете либо определить конструктор перемещения как удаленный, либо опустить его, и в этом случае он не будет существовать, и будет вызван конструктор копирования. - person M.M; 04.08.2015