2015-08-17 3 views
6
class Foo { 
    public: 
    Foo(float b) {} 
}; 

class Bar { 
    public: 
    Bar(Foo foo) {} 
}; 

int main(int argc, char *argv[]) { 
    Bar b1(3.0f); // accept, one implicit convertion happens there. 
    Bar b2 = 3.0f; // error: no viable conversion from 'float' to 'Bar' 
    return 0; 
} 

Почему второе выражение не удается скомпилировать? Я ожидал, что он будет называть тот же самый конструктор преобразования, что и первое выражение.Копирование-инициализация с неявным преобразованием в C++

+0

Попробуйте изменить 'Bar (Foo foo)' to 'Bar (const Foo & foo)' –

+4

@RemyLebeau Почему вы думаете, что он будет работать (это не так)? –

ответ

7

С [dcl.init]:

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

Мы можем вызвать определенное пользователем преобразование, которое от типа источника непосредственно к целевому типу. То есть, если бы у нас было Bar(float), мы рассмотрели бы этот конструктор. Однако в этом случае наш кандидат просто Bar(Foo), который не принимает float.

Вам разрешено ноль или одно пользовательское преобразование. В случае с прямой инициализацией мы просто вызываем Bar(Foo), который вызывает одно пользовательское преобразование (float --> Foo). В случае инициализации копии мы ищем последовательность преобразований от float (тип источника) вплоть до Bar (тип назначения), которая включает в себя две пользовательских конверсий (float --> Foo, Foo --> Bar), следовательно, ошибка.

+1

Когда стандарт говорит «пользовательские последовательности преобразований», что точно учитывается. Например, стандарт определяет такие вещи, как сужение конверсий непосредственно в тексте стандарта. Но как насчет конверсий, определенных в стандартной библиотеке? Обычно вы не говорите, что они «определены пользователем», и я предполагаю, что существуют ограничения на то, что пользователь может делать в пространстве имен std. –

+0

@ChrisBeck Определяемые пользователем преобразования - это те преобразования, которые указаны либо конструкторами (например, «Bar (Foo);»), либо функциями преобразования (например, «operator Foo();'). – Barry

+0

Барри: да, я смотрю сейчас в 12.3.1, он не упоминает о каком-либо другом обращении к стандартной библиотеке, поэтому я думаю, что они считаются «определяемыми пользователем» для этих целей. –

3

Второй тип инициализации называется копированием-инициализацией и использует конструктор копирования. Поэтому этот тип инициализации ожидает, что правая сторона будет преобразована в Bar.

Смежные вопросы