Конструктор преобразования и оператор преобразования: приоритет
чтение некоторых вопросов здесь о so о операторах преобразования и конструкторах заставило меня задуматься о взаимодействии между ними, а именно, когда есть "неоднозначный" вызов. Рассмотрим следующий код:
class A;
class B {
public:
B(){}
B(const A&) //conversion constructor
{
cout << "called B's conversion constructor" << endl;
}
};
class A {
public:
operator B() //conversion operator
{
cout << "called A's conversion operator" << endl;
return B();
}
};
int main()
{
B b = A(); //what should be called here? apparently, A::operator B()
return 0;
}
в приведенном выше коде отображается "called a's conversion operator", что означает, что оператор преобразования вызывается в отличие от конструктора. Если вы удалите / закомментируйте operator B()
код A
, компилятор с радостью переключится на использование конструктора вместо этого (без каких-либо других изменений в коде).
мои вопросы:
- так как компилятор не учитывает
B b = A();
чтобы быть неоднозначным вызовом, здесь должен быть какой-то приоритет. Где именно устанавливается этот приоритет? (ссылка / цитата из стандарта C++ будет оценена) - с объектно-ориентированной философской точки зрения, так ли должен вести себя код? Кто знает больше о том, как
A
объект должно стать
2 ответа:
вы копируете инициализацию, и функции-кандидаты, которые считаются выполняющими преобразования в последовательности преобразования, являются функциями преобразования и конструкторами преобразования. Это в вашем случае
B(const A&) operator B()
вот как вы их объявляете. Разрешение перегрузки абстрагируется от этого и преобразует каждого кандидата в список параметров, соответствующих аргументам вызова. Параметры
B(const A&) B(A&)
второй потому что функция преобразования является функцией-членом. Элемент
A&
-это так называемый неявный параметр объекта, который создается, когда кандидат-это функция-член. Теперь аргумент имеет типA
. При привязке неявного параметра объекта неконстантная ссылка можете привязка к rvalue. Итак, другое правило гласит, что когда у вас есть две жизнеспособные функции, параметры которых являются ссылками, то кандидат, имеющий меньше const квалификация выиграет. Это почему ваша функция преобразования выигрывает. Попробуйте сделатьoperator B
функция-член const. Вы заметите двусмысленность.С объектно-ориентированной философской точки зрения, так ли должен вести себя код? Кто знает больше о том, как объект должен стать объектом B, A или B? Согласно C++, ответ-есть ли что-нибудь в объектно-ориентированной практике, что предполагает, что это должно быть так? Для меня лично это имело бы смысл в любом случае, поэтому мне интересно знаете, как был сделан выбор.
для записи, если вы сделаете функцию преобразования функцией-членом const, то GCC выберет конструктор (так что GCC, похоже, думает, что
B
имеет больше дела с ним?). Переключиться в педантический режим (-pedantic
), чтобы сделать его причиной диагностики.
Standardese,
8.5/14
в противном случае (т. е. для остальных случаев инициализации копирования), определенные пользователем последовательности преобразования, которые могут преобразование из исходного типа в целевой тип или (когда используется функция преобразования) в производный класс перечисляются, как описано в 13.3.1.4, и лучший из них выбирается с помощью разрешения перегрузки (13.3).
и
13.3.1.4
разрешение перегрузки используется для выбора пользовательского преобразования, которое будет вызвано. Предполагая, что "cv1 T" является типом инициализируемого объекта, с типом класса T, функции-кандидаты являются выбрано следующим образом:
- конструкторы преобразования (12.3.1) T являются функциями-кандидатами.
- когда тип выражения инициализатора является типом класса "cv S", рассматриваются функции преобразования S и его базовых классов. Те, которые не скрыты внутри S и дают тип, чья CV-неквалифицированная версия является тем же типом, что и T, или является его производным классом, являются функциями-кандидатами. Функции преобразования, возвращающие "ссылку на X", возвращают значения lvalues типа X и поэтому считаются выходными X для этого процесса выбора функций-кандидатов.
в обоих случаях, список аргументов имеет один аргумент-выражение инициализатор. [Примечание: этот аргумент будет сравниваться с первым параметром конструкторов и с неявным параметром объекта функций преобразования. ]
и
13.3.3.2/3
- стандартное преобразование последовательности S1 является лучше, последовательность конвертации, чем стандартное преобразование последовательности С2, если [...] S1 и S2 являются ссылочными привязками (8.5.3), и типы, на которые ссылаются ссылки, являются одним и тем же типом, за исключением CV-квалификаторов верхнего уровня, а тип, на который ссылается ссылка, инициализированная S2, более квалифицирован cv, чем тип, на который ссылается ссылка, инициализированная S1.
похоже, MSVS2008 имеет свое собственное мнение о выборе конструктора: он вызывает конструктор копирования в B независимо от константы оператора A. Поэтому будьте осторожны здесь, даже если стандарт указывает правильное поведение.
Я думал, что MSVS просто ищет подходящий конструктор перед оператором преобразования, но затем обнаружил, что он начинает вызывать оператор B (), если вы удалите const word из конструктора B. Вероятно, он имеет какое-то особое поведение для временных, потому что следующее код по-прежнему вызывает конструктор B:
A a; B b = a;