При пометке конструктора как удаленного, т.е.
class MyClass {
// ...
MyClass(int x) = delete;
// ...
};
есть ли какой-либо эффект от маркировки удаленного конструктора как explicit
? то есть
explicit MyClass(int x) = delete;
?
Сообщается о другой ошибке: godbolt.org/z/KojesT71d попытка использовать удаленную функцию или отсутствующее преобразование. Возможно, произойдет что-то более интересное, если будет больше перегрузок конструктора или функции, принимающей сконструированный класс.
Другая ортогональная ситуация: компилятор может выдать другое предупреждение в зависимости от того, является ли этот удаленный конструктор (явный или неявный) public:
или нет. Некоторые предупреждения могут оказаться более озадачивающими, чем другие, для программиста, попавшего в ситуацию.
@Eljay: Я всегда публикую удаления, но полагаю, что это тоже может быть вопросом предпочтений.
Да, = delete
не влияет на разрешение перегрузки, за исключением того, что если в конечном итоге разрешение перегрузки выберет удаленную перегрузку, то разрешение перегрузки будет считаться неправильно сформированным. = delete
не изменяет процесс принятия решений по разрешению перегрузки.
explicit
существенно влияет на разрешение перегрузки, поскольку от него зависит, будет ли конструктор участвовать в качестве кандидата в различных сценариях разрешения перегрузки.
Обе конструкции ортогональны. Одно не означает, что следует добавить или исключить другое.
Пример:
struct MyClass {
MyClass(int x) = delete;
MyClass(long x) { };
};
Без explicit
в первом конструкторе MyClass x = 0;
имеет неверный формат, поскольку разрешение перегрузки предпочтет первый конструктор. При использовании explicit
первый конструктор не учитывается при инициализации копирования, а выбирается второй (который в противном случае был бы худшим совпадением). Инициализация не будет неправильной.
С другой стороны, при инициализации формы MyClass x(0);
, которая является прямой инициализацией, всегда учитываются все конструкторы, и при разрешении перегрузки выбирается первый, независимо от того, является ли он explicit
или нет. Поэтому эта инициализация всегда будет неправильной.
Итак, в этом примере вам нужно решить, хотите ли вы выполнить только явное преобразование из int
неправильного формата (при этом разрешая неявное и явное преобразование из long
) или хотите ли вы выполнить неявное и явное преобразование из long
неправильного формата.
Даже если это единственный конструктор класса, разница все равно есть.
Предположим, существует набор перегрузок функций
void f(MyClass);
void f(A);
с
struct MyClass {
explicit MyClass(int x) = delete;
};
struct A {
A(int x) {};
};
Тогда попытка вызвать f(0)
является корректной, поскольку первая перегрузка нежизнеспособна. explicit
конструкторы не учитываются для неявных последовательностей преобразования аргументов функции при разрешении перегрузки. Только вторая перегрузка является жизнеспособной и будет выбрана.
Но если explicit
нет, то обе перегрузки становятся жизнеспособными и разрешение перегрузки будет неоднозначным. То, что конструктор удален, не имеет значения для этого определения.
Удаление конструктора означает, что вы хотите, чтобы конкретный результат разрешения перегрузки привел к неправильному форматированию инициализации. Принимая решение, какую форму вы хотите сделать неправильной, вы всегда должны учитывать, следует ли применять explicit
, точно так же, как при определении неудаляемых перегрузок.
@einpoklum Я уточнил. Я не хочу только сказать, что это не влияет на то, является ли конструктор кандидатом, я хочу сказать, что это вообще не влияет на разрешение перегрузки (за исключением того, что разрешение перегрузки будет неправильно сформировано, если удаленный конструктор будет выбрано).
Может быть, с точки зрения отчета об ошибках?
explicit
может выдать другую ошибку при неявном использовании конструктора и при явном его использовании.