Операторы преобразования в C++
Пожалуйста, помогите мне понять, как именно работают операторы преобразования в C++. У меня есть простой пример, который я пытаюсь понять, хотя не очень понятно, как преобразование происходит на самом деле.
class Example{
public:
Example();
Example(int val);
operator unsigned int();
~Example(){}
private:
int itsVal;
};
Example::Example():itsVal(0){}
Example::Example(int val):itsVal(val){}
Example::operator unsigned int (){
return (itsVal);
}
int main(){
int theInt = 5;
Example exObject = theInt; // here
Example ctr(5);
int theInt1 = ctr; // here
return 0;
}
4 ответа
Вы можете пройти по этому коду с помощью отладчика (и / или поставить точку останова на каждом из ваших конструкторов и операторов), чтобы увидеть, какой из ваших конструкторов и операторов вызывается какими строками.
Поскольку вы не определили их явно, компилятор также создал скрытый / заданный по умолчанию конструктор копирования и оператор присваивания для вашего класса. Вы можете определить их явно (следующим образом), если хотите использовать отладчик, чтобы увидеть, где / когда они вызываются.
Example::Example(const Example& rhs)
: itsVal(rhs.itsVal)
{}
Example& operator=(const Example& rhs)
{
if (this != &rhs)
{
this->itsVal = rhs.itsVal;
}
return *this;
}
int main() {
int theInt = 5;
/**
* Constructor "Example(int val)" in effect at the statement below.
* Same as "Example exObject(theInt);" or "Example exObject = Example(theInt);"
*/
Example exObject = theInt; // 1
Example ctr(5);
/**
* "operator unsigned int()" in effect at the statement below.
* What gets assigned is the value returned by "operator unsigned int()".
*/
int theInt1 = ctr; // 2
return 0;
}
В утверждении 1 конструктор Example(int val)
называется. Объявите это как explicit Example(int val)
и вы получите ошибку времени компиляции, то есть неявное преобразование не будет разрешено для этого конструктора.
Все конструкторы с одним аргументом вызываются неявно, если назначенное значение имеет соответствующий тип аргумента. С использованием explicit
Ключевое слово перед конструкторами с одним аргументом отключает неявный вызов конструктора и, следовательно, неявное преобразование.
Если конструктор был объявлен как явный, т.е. explicit Example(int val)
тогда для каждого утверждения произойдет следующее.
Example exObject(theInt); // Compile time error.
Example exObject = theInt; // Compile time error.
Example exObject(Example(theInt)); // Okay!
Example exObject = Example(theInt); // Okay!
Также обратите внимание, что в случае неявного вызова конструктора и, следовательно, неявного преобразования назначенное значение является rvalue, то есть неименованным объектом, неявно созданным с использованием lvalue (theInt), который говорит нам, что в случае неявного преобразования компилятор преобразует
Example exObject = theInt;
в
Example exObject = Example(theInt);
Поэтому (в C++11) не ожидайте вызова конструктора lvalue, поскольку вы используете lvalue, т.е. именованное значение theInt
для назначения. Вызывается конструктор rvalue, поскольку назначенное значение на самом деле является неназванным объектом, созданным с использованием lvalue. Тем не менее, это применимо, если у вас есть версии конструктора как lvalue, так и rvalue.
В заявлении 2 operator unsigned int()
называется. Просто рассматривайте его как обычный вызов функции со странным именем и тот факт, что он может вызываться автоматически, когда происходит неявное преобразование. Значение, возвращаемое этой функцией, является значением, назначенным в выражении. И поскольку в вашей реализации возвращаемое значение является целым, оно правильно присваивается int theInt1
,
Точнее operator unsigned int()
перегрузки ()
оператор, который является оператором приведения. В вашем случае это перегружено для int
следовательно, всякий раз, когда объект Example
класс присваивается int
неявное приведение типа из Example
в int
имеет место и, следовательно, operator unsigned int()
вызывается. Следовательно,
int theInt1 = ctr;
эквивалентно
int theInt1 = (int)ctr;
Example exObject = theInt; // implicitly created copy constructor takes place
// object implicitly created from int and then copied
// it is like
Example exObject = Example(theInt);
// so it uses sequence
// Example(int) -> Example(const Example&)
int theInt1 = ctr; // operator int()
Если ваш компилятор поддерживает оптимизацию конструктора копирования и оптимизацию возвращаемого значения, вы не заметите
Example(const Example&)
выполнение, но вы можете объявить, что конструктор копирования является частным, чтобы понять, о чем я говорю.
Example exObject = theInt; // here
При этом используется неявное преобразование int в Example, осуществляемое неявным конструктором, который принимает int.
Это также требует доступности конструктора копирования для Example, хотя компилятору разрешено исключать копирование экземпляра.
int theInt1 = ctr; // here
При этом используется неявное преобразование Example в unsigned int, предоставляемое оператором cast.
Операторов приведения обычно избегают, так как они приводят к запутанному коду, и вы можете пометить конструкторы с одним аргументом как явные, чтобы отключить неявные преобразования для вашего типа класса. C++0x должен также добавить возможность явной пометки операторов преобразования (так что вам понадобится static_cast для их вызова? - мой компилятор не поддерживает их, и все веб-ресурсы, похоже, концентрируются на явном преобразовании в bool).