В вызове foo(x)
необходимо создать новый Option
, который станет someVal
во время выполнения тела foo
. То есть x
необходимо скопировать в someVal
. Компилятор пытается инициализировать Option someVal(x);
(сначала Option(Option&)
, затем Option(Option const&)
), но не может, потому что вы сказали, что оба этих конструктора explicit
и не должны вызываться неявно. В C ++ 17 вы можете явно вставить отсутствующий вызов конструктора, чтобы он работал: foo(Option(x))
. До C ++ 17 невозможно вызвать foo
, потому что компилятор будет пытаться вставить вызовы конструктора в Option
, но ни один не доступен для вставки.
На языке стандарта вызов функции, такой как foo(x)
, требует, чтобы параметр someVal
был инициализированным при копировании из x
. При инициализации копирования объекта определенного класса из объекта этого класса или производного класса учитываются только преобразовательные конструкторы этого целевого класса. «Конвертирующий конструктор» - это просто причудливое имя для «конструктора, который не explicit
». Лучший конструктор из них затем выбирается с помощью нормального разрешения перегрузки. Поскольку ни один ваших конструкторов не является explicit
, это всегда дает сбой и foo
не вызывается до C ++ 17. Начиная с C ++ 17, когда аргумент является prvalue (как в foo(Option(x))
), требование об вызове конструктора может быть отменено, и foo
становится вызываемым.
На ваш вопрос:
Кроме того, если есть разница между explicit Option(const Option& other)
и explicit Option(Option& other)
?
Конечно: первый обещает, что не изменит свой аргумент, а второй нет , Вы уже знаете, что они могут быть определены для разных целей, и разрешение перегрузки предпочтет одно над другим, в зависимости от контекста:
Option x(1);
Option const y(2);
Option a(x); // calls Option(Option&) if available, which may modify x; calls Option(Option const&) if not, which shouldn't modify x
Option b(y); // must call Option(Option const&) because that promises not to modify y; cannot call Option(Option&) because it may modify y