Прежде чем продолжить чтение, прочитайте есть ли в 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). Затем преобразованное временное значение prvalue будет использоваться для прямой инициализации возвращаемого объекта, что тоже должно быть в порядке, поскольку прямая инициализация может использовать явный конструктор копирования. Так что 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). Если преобразование не может быть выполнено или неоднозначно, инициализация имеет неправильный формат. Выбранная функция вызывается с выражением инициализатора в качестве аргумента; если функция является конструктором, вызов инициализирует временную версию cv-unqualified целевого типа. Временное является ценностью. Результат вызова (который является временным для случая конструктора) затем используется для прямой инициализации в соответствии с приведенными выше правилами объекта, который является местом назначения инициализации копирования. В некоторых случаях реализации разрешается устранять копирование, присущее этой прямой инициализации, путем создания промежуточного результата непосредственно в инициализируемом объекте; см. 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!
}