Как работает преобразование конструктора?
#include <iostream>
using namespace::std;
class One {
public:
One() { cout<<"One"<<endl;}
};
class Two {
public:
Two(const One&) {cout<<"Two(const One&)"<<endl;}
};
void f(Two) {cout<<"f(Two)"<<endl;}
int main() {
One one;
f(one);
}
производит вывод
One
Two(const One&)
f(Two)
Любой конструктор, который может быть вызван с одним аргументом, считается implicit conversion constructor
, Это включает простые случаи с 1 аргументом и использование аргументов по умолчанию.
Это преобразование рассматривается в любом контексте, который хочет X и предоставил Y, и Y имеет такую неявную возможность преобразования. Обратите внимание, что множество других встроенных преобразований также играют роль микса (например, регулировка константности, интегральных и fp-продвижений, преобразований и т. Д.). Правило состоит в том, что не более одного «определенного пользователем» неявного преобразования разрешено в перемешать.
В некоторых случаях это может быть довольно удивительно, поэтому общий совет — делать любые такие ctors explicit
, Это ключевое слово делает преобразование возможным, но неявным образом: вы должны использовать синтаксис T () для его принудительного применения.
В качестве примера рассмотрим std::vector
у которого ctor принимает size_t, устанавливая начальный размер. Это явно — иначе ваш foo(vector<double> const& )
функция может быть ошибочно вызвана с помощью foo (42).
Это правильный результат. поскольку constructor
не является explicit
— неявное преобразование работает (здесь One
неявно преобразуется в Two
).
one
создается, а затем при передаче f
конвертировано в Two
,
Что за Two(const One&) {cout<<"Two(const One&)"<<endl;}
Конструктор означает, что вы можете построить Two
значение в любое время — неявно — от One
, Когда вы звоните f(one)
он хочет Two
параметр, это дано One
поэтому компилятор соединяет 2 и 2 вместе и говорит: «Я сделаю временный Two
от One
и завершите звонок f()
«… все будут счастливы. Ура!
Компилятор должен создать копию Two
экземпляр в стеке. Когда вы звоните f()
с аргументом, который является объектом класса One
(или любой другой) компилятор смотрит на определение класса Two
и пытается найти конструктор, который принимает One
(или любой другой) объект (или ссылка) в качестве аргумента. Когда такой конструктор найден, он создает объект, используя его. Это называется неявным, потому что компилятор делает это без вашего вмешательства.
class Foo {
public:
Foo(int number) {cout<<"Foo(int number)"<<endl;}
};
void f(Foo) {cout<<"f(Foo)"<<endl;}
int main() {
f(24);
} ///:~
Выход будет:
Foo (внутренний номер)
F (Foo)