Подтвердить что ты не робот

Операторы преобразования в С++

Пожалуйста, помогите мне понять, как работают операторы преобразования на С++. У меня есть простой пример, который я пытаюсь понять, хотя не совсем понятно, как конверсия происходит на самом деле компилятором.

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;
}
4b9b3361

Ответ 1

Вы можете пройти через этот код с помощью отладчика (и/или поставить точку останова для каждого из ваших конструкторов и операторов), чтобы увидеть, какой из ваших конструкторов и операторов вызывается с помощью каких строк.

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

Example::Example(const Example& rhs)
: itsVal(rhs.itsVal)
{}

Example& operator=(const Example& rhs)
{
    if (this != &rhs)
    {
        this->itsVal = rhs.itsVal;
    }
    return *this;
}

Ответ 2

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);

Итак (в С++ 11) не ожидайте, что конструктор lvalue будет вызван, увидев, что вы используете lvalue, то есть именованное значение theInt для назначения. То, что вызывается, является конструктором rvalue, поскольку назначенное значение фактически является неименованным объектом, созданным с использованием lvalue. Однако это применимо, если у вас есть как lvalue, так и rvalue версии конструктора.

В заявлении 2 operator unsigned int() вызывается. Просто рассматривайте его как обычный вызов функции со странным именем и тот факт, что он может автоматически вызваться, когда происходит неявное преобразование. Значение, возвращаемое этой функцией, - это значение, назначенное в выражении. И так как в вашей реализации возвращаемое значение является int, оно правильно присваивается int theInt1.

Точнее operator unsigned int() перегружает оператор (), который является оператором литья. В вашем случае он перегружается для int, следовательно, всякий раз, когда объект класса Example присваивается int, происходит неявное отбрасывание типа от Example до int и, следовательно, вызывается operator unsigned int(). Следовательно,

int theInt1 = ctr;

эквивалентно

int theInt1 = (int)ctr;

Ответ 3

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&)

но вы можете объявить, что конструктор копирования является конфиденциальным, чтобы понять, о чем я говорю.

Ответ 4

Example exObject = theInt; // here

Это использует неявное преобразование int в пример, выполняемое неявным конструктором, который принимает int.

Для этого также требуется наличие конструктора копирования для примера, даже если компилятору разрешено копировать экземпляр.

int theInt1 = ctr; // here

Это использует неявное преобразование примера в unsigned int, предоставляемое оператором литья.

Операторы роли обычно избегают, так как они, как правило, приводят к запутывающему коду, и вы можете указать явные конструкторы с одним аргументом, чтобы отключить неявные преобразования к типу вашего класса. С++ 0x должен добавить также возможность отмечать операции преобразования явно (для этого вам понадобится static_cast для их вызова?), Мой компилятор не поддерживает их, и все веб-ресурсы, похоже, концентрируются на явном преобразовании в bool).