Прежде чем продолжить чтение, прочитайте Есть ли разница в C ++ между инициализацией копирования и прямой инициализацией? Сначала убедитесь, что вы понимаете, о чем идет речь.
Сначала я обобщу правило (см. Стандарт n3225 8.5 / 16, 13.3.1.3, 13.3.1.4 и 13.3.1.5),
1) Для прямой инициализации все конструкторы будут рассматриваться как набор перегрузки, разрешение перегрузки выберет лучшее в соответствии с правилами разрешения перегрузки.
2) Для инициализации копирования и того, что тип источника совпадает с типом назначения или получен из типа назначения, правило такое же, как указано выше, за исключением того, что только преобразователи-конструкторы (конструкторы без явного) будут рассматриваться как набор перегрузки. Это фактически означает, что явные конструкторы копирования / перемещения не будут учитываться в наборе перегрузки.
3) Для случаев инициализации копирования, не включенных в (2) выше (тип источника отличается от типа назначения и не является производным от типа назначения), мы сначала рассмотрим определяемые пользователем последовательности преобразования, которые можно преобразовать из типа источника в место назначения введите или (если используется функция преобразования) к его производному классу. Если преобразование выполнено успешно, результат используется для прямой инициализации целевого объекта.
3.1) Во время этой определенной пользователем последовательности преобразования будут рассматриваться как преобразующие ctors (неявные ctors), так и неявные функции преобразования, в соответствии с правилами в 8.5 / 16 и 13.3.1.4.
3.2) Результат prvalue будет прямой инициализировать целевой объект, как указано в правилах (1), см. 8.5 / 16.
Хорошо, хватит правил, давайте посмотрим на какой-то странный код, который я действительно не знаю, где мои рассуждения неверны, или просто все компиляторы ошибочны. Пожалуйста, помогите мне, спасибо.
struct A
{
A (int) { }
A() { }
explicit A(const A&) { }
};
struct B
{
operator A() { return 2; }
//1) visual c++ and clang passes this
//gcc 4.4.3 denies this, says no viable constructor available
};
int main()
{
B b;
A a = b;
//2) oops, all compilers deny this
}
В моем понимании, за (1),
operator A() { return 2; }
Поскольку в C ++ есть правило, что возвращение функции принимается как инициализация копирования, согласно правилу выше, 2 будет сначала неявно преобразовано в A, что должно быть в порядке, потому что A имеет конструктор A (int). Затем преобразованное временное значение будет использоваться для прямой инициализации возвращаемого объекта, что тоже должно быть в порядке, поскольку прямая инициализация может использовать явный конструктор копирования. Так что GCC не прав.
Для (2),
A a = b;
В моем понимании, сначала b неявно преобразуется в A оператором A (), а затем преобразованное значение должно использоваться для прямой инициализации a, что, конечно, может вызвать явный конструктор копирования? Таким образом, это должно пройти компиляцию и все компиляторы ошибочны?
Обратите внимание, что для (2) и Visual C ++, и Clang имеет ошибку, аналогичную,
«Ошибка, невозможно преобразовать из B в A», но если я удалю явное ключевое слово в конструкторе копирования A, ошибка исчезнет ..
Спасибо за чтение.
редактировать 1
Поскольку кто-то все еще не понял, что я имел в виду, я цитирую следующий стандарт из 8.5 / 16,
В противном случае (т.е. для остальных
случаи инициализации копии),
определяемые пользователем последовательности преобразования, которые
можно преобразовать из типа источника в
тип назначения или (когда
функция преобразования используется) в
их производные классы перечислены
как описано в 13.3.1.4, и лучший
один выбирается из-за перегрузки
разрешение (13,3). Если преобразование
не может быть сделано или является неоднозначным,
инициализация плохо сформирована.
выбранная функция вызывается с
выражение инициализатора как его
аргумент; если функция
конструктор, вызов инициализирует
временный из резюме
версия типа назначения.
временное является prvalue. Результат
вызов (который является временным для
случай конструктора) затем используется для
прямая инициализация, согласно
правила выше, объект, который является
пункт назначения
копирования инициализации. В определенных случаяхреализация разрешена
устранить копирования, присущие этому
прямая инициализация путем построения
промежуточный результат непосредственно в
инициализируемый объект; увидеть
12,2, 12,8.
Обратите внимание, что в нем упоминается прямая инициализация после пользовательского преобразования. Что означает, в моем понимании, следующий код должен подчиняться правилам, как я прокомментировал, что подтверждается как clang, coomeau online, visual c ++, но GCC 4.4.3 не справляется как (1), так и (2). Хотя это странное правило, но оно следует из стандартов.
struct A
{
A (int) { }
A() { }
explicit A(const A&) { }
};
int main()
{
A a = 2; //1)OK, first convert, then direct-initialize
A a = (A)2; //2)oops, constructor explicit, not viable here!
}