Операторы преобразования в 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).

Другие вопросы по тегам